1
0
Protocol/packets/protopacket.go

282 lines
6.4 KiB
Go

package packets
import (
"encoding/binary"
"time"
)
// ProtoPacket handles low-level protocol features including EQ2-specific operations
// Merges functionality from EQProtocolPacket and EQ2Packet
type ProtoPacket struct {
*Packet
// Protocol state flags (using bitfield)
flags uint8 // bit 0: compressed, bit 1: prepared, bit 2: encrypted, bit 3: acked
// EQ2-specific
LoginOp EmuOpcode // From EQ2Packet
// Reliability and sequencing
Sequence int32
SentTime int32
AttemptCount int8
}
// Protocol flag constants
const (
FlagCompressed = 1 << iota
FlagPrepared
FlagEncrypted
FlagAcked
)
// NewProtoPacket creates a protocol packet with opcode and buffer
func NewProtoPacket(op uint16, buf []byte) *ProtoPacket {
return &ProtoPacket{
Packet: NewPacket(op, buf),
}
}
// NewProtoPacketFromRaw creates a protocol packet from raw buffer
func NewProtoPacketFromRaw(buf []byte, opcodeOverride int) *ProtoPacket {
var offset uint32
var opcode uint16
if opcodeOverride >= 0 {
opcode = uint16(opcodeOverride)
} else if len(buf) >= 2 {
opcode = binary.BigEndian.Uint16(buf[:2])
offset = 2
}
var data []byte
if uint32(len(buf)) > offset {
data = make([]byte, len(buf)-int(offset))
copy(data, buf[offset:])
}
return &ProtoPacket{
Packet: &Packet{
Opcode: opcode,
Buffer: data,
Timestamp: time.Now(),
},
}
}
// IsCompressed returns true if packet is compressed
func (p *ProtoPacket) IsCompressed() bool {
return p.flags&FlagCompressed != 0
}
// SetCompressed sets the compressed flag
func (p *ProtoPacket) SetCompressed(compressed bool) {
if compressed {
p.flags |= FlagCompressed
} else {
p.flags &^= FlagCompressed
}
}
// IsPrepared returns true if packet has been prepared for sending
func (p *ProtoPacket) IsPrepared() bool {
return p.flags&FlagPrepared != 0
}
// SetPrepared sets the prepared flag
func (p *ProtoPacket) SetPrepared(prepared bool) {
if prepared {
p.flags |= FlagPrepared
} else {
p.flags &^= FlagPrepared
}
}
// IsEncrypted returns true if packet is encrypted
func (p *ProtoPacket) IsEncrypted() bool {
return p.flags&FlagEncrypted != 0
}
// SetEncrypted sets the encrypted flag
func (p *ProtoPacket) SetEncrypted(encrypted bool) {
if encrypted {
p.flags |= FlagEncrypted
} else {
p.flags &^= FlagEncrypted
}
}
// IsAcked returns true if packet has been acknowledged
func (p *ProtoPacket) IsAcked() bool {
return p.flags&FlagAcked != 0
}
// SetAcked sets the acknowledged flag
func (p *ProtoPacket) SetAcked(acked bool) {
if acked {
p.flags |= FlagAcked
} else {
p.flags &^= FlagAcked
}
}
// Copy creates a deep copy of this protocol packet
func (p *ProtoPacket) Copy() *ProtoPacket {
newPacket := &ProtoPacket{
Packet: NewPacket(p.Opcode, p.Buffer),
flags: p.flags,
LoginOp: p.LoginOp,
Sequence: p.Sequence,
SentTime: p.SentTime,
AttemptCount: p.AttemptCount,
}
newPacket.Packet.CopyInfo(p.Packet)
return newPacket
}
// Serialize writes the protocol packet to a destination buffer
func (p *ProtoPacket) Serialize(dest []byte, offset int8) uint32 {
// Write opcode (2 bytes)
if p.Opcode > 0xff {
binary.BigEndian.PutUint16(dest, p.Opcode)
} else {
dest[0] = 0
dest[1] = byte(p.Opcode)
}
// Copy packet data after opcode
if offset < int8(len(p.Buffer)) {
copy(dest[2:], p.Buffer[offset:])
}
return uint32(len(p.Buffer)-int(offset)) + 2
}
// AppCombine combines this packet with another for efficient transmission (from EQ2Packet)
func (p *ProtoPacket) AppCombine(rhs *ProtoPacket) bool {
const opAppCombined = 0x19 // OP_AppCombined value
// Case 1: This packet is already a combined packet
if p.Opcode == opAppCombined && (len(p.Buffer)+len(rhs.Buffer)+3) < 255 {
tmpSize := len(rhs.Buffer) - 2
overSized := tmpSize >= 255
var newSize int
if overSized {
newSize = len(p.Buffer) + tmpSize + 3
} else {
newSize = len(p.Buffer) + tmpSize + 1
}
tmpBuffer := make([]byte, newSize)
pos := 0
// Copy existing combined packet data
copy(tmpBuffer, p.Buffer)
pos += len(p.Buffer)
// Add size information for the new packet
if overSized {
tmpBuffer[pos] = 255
pos++
binary.BigEndian.PutUint16(tmpBuffer[pos:], uint16(tmpSize))
pos += 2
} else {
tmpBuffer[pos] = byte(tmpSize)
pos++
}
// Copy the new packet data (skip first 2 bytes which are opcode)
if len(rhs.Buffer) > 2 {
copy(tmpBuffer[pos:], rhs.Buffer[2:])
}
p.Buffer = tmpBuffer
return true
}
// Case 2: Create new combined packet
// Implementation would continue here for other combine cases
// Simplified for brevity
return false
}
// PreparePacket prepares an EQ2 packet for transmission (from EQ2Packet)
func (p *ProtoPacket) PreparePacket(maxLen int16) int8 {
if p.IsPrepared() {
return 0
}
p.SetPrepared(true)
// Convert emulator opcode to network opcode
// This would use opcode manager in full implementation
loginOpcode := p.LoginOp // Simplified - would do actual conversion
var offset int8
newSize := len(p.Buffer) + 2 + 1 // sequence(2) + compressed_flag(1)
oversized := false
// Handle different opcode sizes and formats
if loginOpcode != 2 {
newSize++ // opcode type byte
if loginOpcode >= 255 {
newSize += 2 // oversized opcode
oversized = true
}
}
// Build new packet buffer
newBuffer := make([]byte, newSize)
ptr := 2 // Skip sequence field
if loginOpcode != 2 {
if oversized {
ptr++ // Skip compressed flag position
newBuffer[ptr] = 0xff // Oversized marker
ptr++
binary.BigEndian.PutUint16(newBuffer[ptr:], uint16(loginOpcode))
ptr += 2
} else {
binary.BigEndian.PutUint16(newBuffer[ptr:], uint16(loginOpcode))
ptr += 2
}
} else {
newBuffer[ptr] = byte(loginOpcode)
ptr++
}
// Copy original packet data
copy(newBuffer[ptr:], p.Buffer)
p.Buffer = newBuffer
offset = int8(newSize - len(p.Buffer) - 1)
return offset
}
// MakeApplicationPacket converts protocol packet to application packet
func (p *ProtoPacket) MakeApplicationPacket(opcodeSize uint8) *AppPacket {
if opcodeSize == 0 {
opcodeSize = DefaultOpcodeSize
}
app := &AppPacket{
Packet: NewPacket(0, p.Buffer),
opcodeSize: opcodeSize,
}
app.CopyInfo(p.Packet)
// Parse opcode from buffer based on size
if opcodeSize == 1 && len(p.Buffer) >= 1 {
app.Opcode = uint16(p.Buffer[0])
app.Buffer = p.Buffer[1:]
} else if len(p.Buffer) >= 2 {
app.Opcode = binary.BigEndian.Uint16(p.Buffer[:2])
app.Buffer = p.Buffer[2:]
}
return app
}