282 lines
6.4 KiB
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
|
|
}
|