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 }