package eq2net import ( "encoding/binary" "time" ) // handleIncomingPacket processes incoming network packets func (s *EQStream) handleIncomingPacket(data []byte) { // Update statistics s.stats.PacketsReceived.Add(1) s.stats.BytesReceived.Add(uint64(len(data))) // Validate minimum size if len(data) < 2 { return } // Extract opcode opcode := binary.BigEndian.Uint16(data[0:2]) // Check CRC for non-exempt packets if !s.isCRCExempt(opcode) { if !ValidateCRC(data, s.config.CRCKey) { s.stats.PacketsDropped.Add(1) return } // Remove CRC bytes for further processing if len(data) > 2 { data = data[:len(data)-2] } } // Create protocol packet packet, err := NewEQProtocolPacketFromBuffer(data, -1) if err != nil { return } // Handle based on opcode switch packet.Opcode { case OPSessionRequest: s.handleSessionRequest(packet) case OPSessionResponse: s.handleSessionResponse(packet) case OPSessionDisconnect: s.handleDisconnect(packet) case OPKeepAlive: s.handleKeepAlive(packet) case OPAck: s.handleAck(packet) case OPOutOfOrderAck: s.handleOutOfOrderAck(packet) case OPPacket: s.handleDataPacket(packet) case OPFragment: s.handleFragment(packet) case OPCombined: s.handleCombined(packet) case OPOutOfSession: s.handleOutOfSession(packet) default: // Unknown opcode, ignore } } // handleSessionRequest processes incoming session requests func (s *EQStream) handleSessionRequest(packet *EQProtocolPacket) { if s.GetState() != StreamStateDisconnected { // We're not accepting new connections s.sendSessionResponse(false) return } // Parse request if len(packet.Buffer) < 10 { return } version := binary.BigEndian.Uint32(packet.Buffer[0:4]) sessionID := binary.BigEndian.Uint32(packet.Buffer[4:8]) maxLength := binary.BigEndian.Uint16(packet.Buffer[8:10]) // Validate version if version != 2 { s.sendSessionResponse(false) return } // Update session info s.sessionID = sessionID if int(maxLength) < s.config.MaxPacketSize { s.config.MaxPacketSize = int(maxLength) } // Accept connection s.state.Store(int32(StreamStateConnected)) s.sendSessionResponse(true) if s.onConnect != nil { s.onConnect() } } // handleSessionResponse processes session response packets func (s *EQStream) handleSessionResponse(packet *EQProtocolPacket) { if s.GetState() != StreamStateConnecting { return } // Parse response if len(packet.Buffer) < 11 { return } sessionID := binary.BigEndian.Uint32(packet.Buffer[0:4]) crcKey := binary.BigEndian.Uint32(packet.Buffer[4:8]) validation := packet.Buffer[8] format := packet.Buffer[9] unknownByte := packet.Buffer[10] // Check if accepted if validation == 0 { // Connection rejected s.state.Store(int32(StreamStateDisconnected)) if s.onDisconnect != nil { s.onDisconnect("connection rejected") } return } // Update session info s.sessionID = sessionID s.config.CRCKey = crcKey _ = format // Store for later use if needed _ = unknownByte // Connection established s.state.Store(int32(StreamStateConnected)) } // handleDisconnect processes disconnect packets func (s *EQStream) handleDisconnect(packet *EQProtocolPacket) { s.state.Store(int32(StreamStateDisconnecting)) // Send acknowledgment ack := NewEQProtocolPacket(OPSessionDisconnect, nil) s.sendPacketNow(ack) // Clean shutdown if s.onDisconnect != nil { s.onDisconnect("remote disconnect") } s.Close() } // handleKeepAlive processes keep-alive packets func (s *EQStream) handleKeepAlive(packet *EQProtocolPacket) { // Keep-alives don't require a response, just update last activity time // This helps detect dead connections } // handleAck processes acknowledgment packets func (s *EQStream) handleAck(packet *EQProtocolPacket) { if len(packet.Buffer) < 2 { return } ackSeq := binary.BigEndian.Uint16(packet.Buffer[0:2]) s.processAck(uint32(ackSeq)) } // handleOutOfOrderAck processes out-of-order acknowledgments func (s *EQStream) handleOutOfOrderAck(packet *EQProtocolPacket) { if len(packet.Buffer) < 2 { return } ackSeq := binary.BigEndian.Uint16(packet.Buffer[0:2]) s.processAck(uint32(ackSeq)) } // processAck handles acknowledgment of a sent packet func (s *EQStream) processAck(seq uint32) { s.sendWindowMu.Lock() defer s.sendWindowMu.Unlock() if sp, exists := s.sendWindow[seq]; exists { // Calculate RTT and update estimates rtt := time.Since(sp.sentTime).Microseconds() s.updateRTT(rtt) // Remove from send window delete(s.sendWindow, seq) // Update last acknowledged sequence if seq > s.lastAckSeq.Load() { s.lastAckSeq.Store(seq) } } } // updateRTT updates the RTT estimates using Jacobson/Karels algorithm func (s *EQStream) updateRTT(sampleRTT int64) { // First sample if s.rtt.Load() == 0 { s.rtt.Store(sampleRTT) s.rttVar.Store(sampleRTT / 2) s.rto.Store(sampleRTT + 4*s.rttVar.Load()) return } // Subsequent samples (RFC 6298) alpha := int64(125) // 1/8 in fixed point (multiply by 1000) beta := int64(250) // 1/4 in fixed point // SRTT = (1-alpha) * SRTT + alpha * RTT srtt := s.rtt.Load() srtt = ((1000-alpha)*srtt + alpha*sampleRTT) / 1000 // RTTVAR = (1-beta) * RTTVAR + beta * |SRTT - RTT| var diff int64 if srtt > sampleRTT { diff = srtt - sampleRTT } else { diff = sampleRTT - srtt } rttVar := s.rttVar.Load() rttVar = ((1000-beta)*rttVar + beta*diff) / 1000 // RTO = SRTT + 4 * RTTVAR rto := srtt + 4*rttVar // Minimum RTO of 200ms, maximum of 60s if rto < 200000 { rto = 200000 } else if rto > 60000000 { rto = 60000000 } s.rtt.Store(srtt) s.rttVar.Store(rttVar) s.rto.Store(rto) s.stats.RTT.Store(srtt) } // handleDataPacket processes regular data packets func (s *EQStream) handleDataPacket(packet *EQProtocolPacket) { // Extract sequence number (first 2 bytes after opcode) if len(packet.Buffer) < 2 { return } seq := uint32(binary.BigEndian.Uint16(packet.Buffer[0:2])) // Send acknowledgment s.sendAck(seq) // Check if it's in order expectedSeq := s.nextSeqIn.Load() if seq == expectedSeq { // In order, process immediately s.nextSeqIn.Add(1) s.processDataPacket(packet.Buffer[2:]) // Check if we have any queued packets that can now be processed s.processQueuedPackets() } else if seq > expectedSeq { // Future packet, queue it s.recvWindowMu.Lock() s.recvWindow[seq] = packet s.recvWindowMu.Unlock() // Send out-of-order ACK s.sendOutOfOrderAck(seq) } // If seq < expectedSeq, it's a duplicate, ignore (we already sent ACK) } // processQueuedPackets processes any queued packets that are now in order func (s *EQStream) processQueuedPackets() { for { expectedSeq := s.nextSeqIn.Load() s.recvWindowMu.Lock() packet, exists := s.recvWindow[expectedSeq] if !exists { s.recvWindowMu.Unlock() break } delete(s.recvWindow, expectedSeq) s.recvWindowMu.Unlock() s.nextSeqIn.Add(1) if len(packet.Buffer) > 2 { s.processDataPacket(packet.Buffer[2:]) } } } // processDataPacket processes the data portion of a packet func (s *EQStream) processDataPacket(data []byte) { // Decompress if needed if IsCompressed(data) { decompressed, err := DecompressPacket(data) if err != nil { return } data = decompressed } // Decrypt chat if needed (check for chat opcodes) // This would need opcode inspection // Convert to application packet if len(data) < 2 { return } app := &EQApplicationPacket{ EQPacket: NewEQPacket(binary.BigEndian.Uint16(data[0:2]), nil), } if len(data) > 2 { app.Buffer = make([]byte, len(data)-2) copy(app.Buffer, data[2:]) app.Size = uint32(len(app.Buffer)) } // Queue for application select { case s.recvQueue <- app: default: // Receive queue full, drop packet s.stats.PacketsDropped.Add(1) } } // handleFragment processes fragmented packets func (s *EQStream) handleFragment(packet *EQProtocolPacket) { if len(packet.Buffer) < 6 { return } // Parse fragment header seq := uint32(binary.BigEndian.Uint16(packet.Buffer[0:2])) totalSize := binary.BigEndian.Uint32(packet.Buffer[2:6]) // Send acknowledgment s.sendAck(seq) // Store fragment s.recvWindowMu.Lock() s.fragmentQueue[seq] = append(s.fragmentQueue[seq], packet) // Check if we have all fragments currentSize := uint32(0) for _, frag := range s.fragmentQueue[seq] { if len(frag.Buffer) > 6 { currentSize += uint32(len(frag.Buffer) - 6) } } if currentSize >= totalSize { // Reassemble packet reassembled := make([]byte, 0, totalSize) for _, frag := range s.fragmentQueue[seq] { if len(frag.Buffer) > 6 { reassembled = append(reassembled, frag.Buffer[6:]...) } } // Clean up fragment queue delete(s.fragmentQueue, seq) s.recvWindowMu.Unlock() // Process reassembled packet s.processDataPacket(reassembled) } else { s.recvWindowMu.Unlock() } } // handleCombined processes combined packets func (s *EQStream) handleCombined(packet *EQProtocolPacket) { data := packet.Buffer offset := 0 for offset < len(data) { if offset+1 > len(data) { break } // Get sub-packet size size := int(data[offset]) offset++ // Handle oversized packets (size == 255) if size == 255 && offset+2 <= len(data) { size = int(binary.BigEndian.Uint16(data[offset:offset+2])) offset += 2 } if offset+size > len(data) { break } // Process sub-packet subData := data[offset : offset+size] s.handleIncomingPacket(subData) offset += size } } // handleOutOfSession processes out-of-session packets func (s *EQStream) handleOutOfSession(packet *EQProtocolPacket) { // Server is telling us we're not in a session s.state.Store(int32(StreamStateDisconnected)) if s.onDisconnect != nil { s.onDisconnect("out of session") } } // sendAck sends an acknowledgment packet func (s *EQStream) sendAck(seq uint32) { data := make([]byte, 2) binary.BigEndian.PutUint16(data, uint16(seq)) ack := NewEQProtocolPacket(OPAck, data) s.sendPacketNow(ack) } // sendOutOfOrderAck sends an out-of-order acknowledgment func (s *EQStream) sendOutOfOrderAck(seq uint32) { data := make([]byte, 2) binary.BigEndian.PutUint16(data, uint16(seq)) ack := NewEQProtocolPacket(OPOutOfOrderAck, data) s.sendPacketNow(ack) } // sendSessionResponse sends a session response packet func (s *EQStream) sendSessionResponse(accept bool) { data := make([]byte, 11) binary.BigEndian.PutUint32(data[0:4], s.sessionID) binary.BigEndian.PutUint32(data[4:8], s.config.CRCKey) if accept { data[8] = 1 // Validation byte } else { data[8] = 0 // Rejection } data[9] = 0 // Format data[10] = 0 // Unknown response := NewEQProtocolPacket(OPSessionResponse, data) s.sendPacketNow(response) } // FragmentPacket breaks a large packet into fragments func (s *EQStream) FragmentPacket(data []byte, maxSize int) []*EQProtocolPacket { if len(data) <= maxSize { // No fragmentation needed return []*EQProtocolPacket{NewEQProtocolPacket(OPPacket, data)} } // Calculate fragment sizes headerSize := 6 // seq(2) + total_size(4) fragmentDataSize := maxSize - headerSize numFragments := (len(data) + fragmentDataSize - 1) / fragmentDataSize fragments := make([]*EQProtocolPacket, 0, numFragments) totalSize := uint32(len(data)) for offset := 0; offset < len(data); offset += fragmentDataSize { end := offset + fragmentDataSize if end > len(data) { end = len(data) } // Build fragment packet fragData := make([]byte, headerSize+end-offset) // Sequence will be set by send worker binary.BigEndian.PutUint32(fragData[2:6], totalSize) copy(fragData[6:], data[offset:end]) fragments = append(fragments, NewEQProtocolPacket(OPFragment, fragData)) } return fragments }