1
0
Protocol/packet.go
2025-09-01 13:45:40 -05:00

299 lines
6.8 KiB
Go

// Package eq2net implements the EverQuest 2 network protocol
package eq2net
import (
"encoding/binary"
"fmt"
"net"
"time"
)
// Protocol opcodes for low-level packet control
const (
OPSessionRequest = 0x0001
OPSessionResponse = 0x0002
OPCombined = 0x0003
OPSessionDisconnect = 0x0005
OPKeepAlive = 0x0006
OPSessionStatRequest = 0x0007
OPSessionStatResponse = 0x0008
OPPacket = 0x0009
OPFragment = 0x000D
OPOutOfOrderAck = 0x0011
OPAck = 0x0015
OPAppCombined = 0x0019
OPOutOfSession = 0x001D
)
// EQPacket is the base packet type for all EverQuest packets
type EQPacket struct {
// Core packet data
Buffer []byte
Size uint32
Opcode uint16
// Network information
SrcIP net.IP
DstIP net.IP
SrcPort uint16
DstPort uint16
// Metadata
Priority uint32
Timestamp time.Time
Version int16
}
// NewEQPacket creates a new packet with the specified opcode and data
func NewEQPacket(opcode uint16, data []byte) *EQPacket {
p := &EQPacket{
Opcode: opcode,
Timestamp: time.Now(),
}
if len(data) > 0 {
p.Buffer = make([]byte, len(data))
copy(p.Buffer, data)
p.Size = uint32(len(data))
}
return p
}
// TotalSize returns the total packet size including opcode
func (p *EQPacket) TotalSize() uint32 {
return p.Size + 2 // +2 for opcode
}
// SetNetworkInfo sets the source and destination network information
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
}
// CopyInfo copies network and timing information from another packet
func (p *EQPacket) CopyInfo(other *EQPacket) {
p.SrcIP = other.SrcIP
p.SrcPort = other.SrcPort
p.DstIP = other.DstIP
p.DstPort = other.DstPort
p.Timestamp = other.Timestamp
p.Version = other.Version
}
// EQProtocolPacket handles low-level protocol operations
type EQProtocolPacket struct {
*EQPacket
// Protocol state flags
Compressed bool
Prepared bool
Encrypted bool
Acked bool
// Reliability tracking
SentTime time.Time
AttemptCount uint8
Sequence uint32
}
// NewEQProtocolPacket creates a new protocol packet
func NewEQProtocolPacket(opcode uint16, data []byte) *EQProtocolPacket {
return &EQProtocolPacket{
EQPacket: NewEQPacket(opcode, data),
}
}
// NewEQProtocolPacketFromBuffer creates a protocol packet from raw buffer
func NewEQProtocolPacketFromBuffer(buffer []byte, opcodeOverride int) (*EQProtocolPacket, error) {
if len(buffer) < 2 {
return nil, fmt.Errorf("buffer too small for opcode")
}
var opcode uint16
var dataOffset int
if opcodeOverride >= 0 {
opcode = uint16(opcodeOverride)
dataOffset = 0
} else {
opcode = binary.BigEndian.Uint16(buffer[:2])
dataOffset = 2
}
var data []byte
if len(buffer) > dataOffset {
data = buffer[dataOffset:]
}
return NewEQProtocolPacket(opcode, data), nil
}
// Serialize writes the protocol packet to a byte buffer
func (p *EQProtocolPacket) Serialize(offset int) []byte {
// Allocate buffer for opcode + data
result := make([]byte, 2+len(p.Buffer)-offset)
// Write opcode (big-endian)
if p.Opcode > 0xFF {
binary.BigEndian.PutUint16(result[0:2], p.Opcode)
} else {
result[0] = 0
result[1] = byte(p.Opcode)
}
// Copy packet data
if len(p.Buffer) > offset {
copy(result[2:], p.Buffer[offset:])
}
return result
}
// IsProtocolPacket checks if the opcode is a valid protocol packet
func IsProtocolPacket(opcode uint16) bool {
switch opcode {
case OPSessionRequest, OPSessionDisconnect, OPKeepAlive,
OPSessionStatResponse, OPPacket, OPCombined, OPFragment,
OPAck, OPOutOfOrderAck, OPOutOfSession:
return true
default:
return false
}
}
// Copy creates a deep copy of the protocol packet
func (p *EQProtocolPacket) Copy() *EQProtocolPacket {
newPacket := &EQProtocolPacket{
EQPacket: NewEQPacket(p.Opcode, p.Buffer),
Compressed: p.Compressed,
Prepared: p.Prepared,
Encrypted: p.Encrypted,
Acked: p.Acked,
SentTime: p.SentTime,
AttemptCount: p.AttemptCount,
Sequence: p.Sequence,
}
newPacket.CopyInfo(p.EQPacket)
return newPacket
}
// EQApplicationPacket represents high-level application packets
type EQApplicationPacket struct {
*EQPacket
// Cached emulator opcode
EmuOpcode uint16
// Opcode size (1 or 2 bytes)
OpcodeSize uint8
}
// DefaultOpcodeSize is the default size for application opcodes
var DefaultOpcodeSize uint8 = 2
// NewEQApplicationPacket creates a new application packet
func NewEQApplicationPacket(opcode uint16, data []byte) *EQApplicationPacket {
return &EQApplicationPacket{
EQPacket: NewEQPacket(opcode, data),
EmuOpcode: opcode,
OpcodeSize: DefaultOpcodeSize,
}
}
// Serialize writes the application packet to a byte buffer
func (p *EQApplicationPacket) Serialize() []byte {
opcodeBytes := p.OpcodeSize
// Special handling for opcodes with low byte = 0x00
if p.OpcodeSize == 2 && (p.Opcode&0x00FF) == 0 {
opcodeBytes = 3
}
result := make([]byte, uint32(opcodeBytes)+p.Size)
if p.OpcodeSize == 1 {
result[0] = byte(p.Opcode)
} else {
if (p.Opcode & 0x00FF) == 0 {
result[0] = 0
binary.BigEndian.PutUint16(result[1:3], p.Opcode)
} else {
binary.BigEndian.PutUint16(result[0:2], p.Opcode)
}
}
// Copy data after opcode
if p.Size > 0 {
copy(result[opcodeBytes:], p.Buffer)
}
return result
}
// Copy creates a deep copy of the application packet
func (p *EQApplicationPacket) Copy() *EQApplicationPacket {
newPacket := &EQApplicationPacket{
EQPacket: NewEQPacket(p.Opcode, p.Buffer),
EmuOpcode: p.EmuOpcode,
OpcodeSize: p.OpcodeSize,
}
newPacket.CopyInfo(p.EQPacket)
return newPacket
}
// PacketCombiner handles combining multiple packets for efficient transmission
type PacketCombiner struct {
maxSize int
}
// NewPacketCombiner creates a new packet combiner with max size limit
func NewPacketCombiner(maxSize int) *PacketCombiner {
return &PacketCombiner{maxSize: maxSize}
}
// CombineProtocolPackets combines multiple protocol packets into one
func (c *PacketCombiner) CombineProtocolPackets(packets []*EQProtocolPacket) *EQProtocolPacket {
if len(packets) == 0 {
return nil
}
if len(packets) == 1 {
return packets[0]
}
// Calculate total size needed
totalSize := 0
for _, p := range packets {
totalSize += 1 + int(p.TotalSize()) // 1 byte for size prefix
}
if totalSize > c.maxSize {
return nil // Too large to combine
}
// Build combined packet buffer
buffer := make([]byte, totalSize)
offset := 0
for _, p := range packets {
// Write size prefix
buffer[offset] = byte(p.TotalSize())
offset++
// Serialize packet
serialized := p.Serialize(0)
copy(buffer[offset:], serialized)
offset += len(serialized)
}
// Create combined packet
combined := NewEQProtocolPacket(OPCombined, buffer)
if len(packets) > 0 {
combined.CopyInfo(packets[0].EQPacket)
}
return combined
}