convert packet management
This commit is contained in:
parent
6612d0731f
commit
a1dd39d45a
120
internal/packets/app.go
Normal file
120
internal/packets/app.go
Normal file
@ -0,0 +1,120 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"eq2emu/internal/opcodes"
|
||||
)
|
||||
|
||||
type AppHandler struct {
|
||||
pool *PacketPool
|
||||
}
|
||||
|
||||
func NewAppHandler(pool *PacketPool) *AppHandler {
|
||||
return &AppHandler{pool: pool}
|
||||
}
|
||||
|
||||
// Create application packet for sending
|
||||
func (h *AppHandler) CreateAppPacket(opcode opcodes.AppOpcode, data []byte) *AppPacket {
|
||||
packet := h.pool.GetAppPacket()
|
||||
packet.Opcode = opcode
|
||||
packet.Data = make([]byte, len(data))
|
||||
copy(packet.Data, data)
|
||||
return packet
|
||||
}
|
||||
|
||||
// Serialize app packet to protocol packet
|
||||
func (h *AppHandler) SerializeAppPacket(app *AppPacket, sequence uint16) []byte {
|
||||
size := 3 + len(app.Data) // opcode(1) + seq(2) + opcode(2) + data
|
||||
if app.Compressed {
|
||||
size++ // compression flag
|
||||
}
|
||||
|
||||
data := make([]byte, size)
|
||||
offset := 0
|
||||
|
||||
data[offset] = byte(opcodes.OP_Packet)
|
||||
offset++
|
||||
|
||||
binary.LittleEndian.PutUint16(data[offset:], sequence)
|
||||
offset += 2
|
||||
|
||||
if app.Compressed {
|
||||
data[offset] = 0xFF
|
||||
offset++
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint16(data[offset:], uint16(app.Opcode))
|
||||
offset += 2
|
||||
|
||||
copy(data[offset:], app.Data)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// Create combined packet
|
||||
func (h *AppHandler) SerializeCombinedPacket(apps []*AppPacket, sequence uint16) []byte {
|
||||
// Calculate total size
|
||||
totalSize := 3 // opcode + sequence
|
||||
for _, app := range apps {
|
||||
subSize := 2 + len(app.Data) // opcode + data
|
||||
if subSize < 0xFF {
|
||||
totalSize += 1 + subSize
|
||||
} else {
|
||||
totalSize += 3 + subSize
|
||||
}
|
||||
}
|
||||
|
||||
data := make([]byte, totalSize)
|
||||
offset := 0
|
||||
|
||||
data[offset] = byte(opcodes.OP_Combined)
|
||||
offset++
|
||||
|
||||
binary.LittleEndian.PutUint16(data[offset:], sequence)
|
||||
offset += 2
|
||||
|
||||
for _, app := range apps {
|
||||
subSize := 2 + len(app.Data)
|
||||
|
||||
// Write length
|
||||
if subSize < 0xFF {
|
||||
data[offset] = byte(subSize)
|
||||
offset++
|
||||
} else {
|
||||
data[offset] = 0xFF
|
||||
offset++
|
||||
binary.LittleEndian.PutUint16(data[offset:], uint16(subSize))
|
||||
offset += 2
|
||||
}
|
||||
|
||||
// Write opcode
|
||||
binary.LittleEndian.PutUint16(data[offset:], uint16(app.Opcode))
|
||||
offset += 2
|
||||
|
||||
// Write data
|
||||
copy(data[offset:], app.Data)
|
||||
offset += len(app.Data)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// Common EQ2 packet helpers
|
||||
func (h *AppHandler) CreateChatPacket(message string, channel uint8) *AppPacket {
|
||||
writer := NewPacketWriter()
|
||||
writer.WriteUint8(channel)
|
||||
writer.WriteEQ2String(message)
|
||||
|
||||
return h.CreateAppPacket(opcodes.OP_EqHearChatCmd, writer.Bytes())
|
||||
}
|
||||
|
||||
func (h *AppHandler) CreatePositionUpdatePacket(playerID uint32, x, y, z float32) *AppPacket {
|
||||
writer := NewPacketWriter()
|
||||
writer.WriteUint32(playerID)
|
||||
writer.WriteFloat32(x)
|
||||
writer.WriteFloat32(y)
|
||||
writer.WriteFloat32(z)
|
||||
|
||||
return h.CreateAppPacket(opcodes.OP_UpdatePositionMsg, writer.Bytes())
|
||||
}
|
64
internal/packets/compression.go
Normal file
64
internal/packets/compression.go
Normal file
@ -0,0 +1,64 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"io"
|
||||
)
|
||||
|
||||
type EQ2Compressor struct {
|
||||
level int
|
||||
}
|
||||
|
||||
func NewEQ2Compressor() *EQ2Compressor {
|
||||
return &EQ2Compressor{level: zlib.DefaultCompression}
|
||||
}
|
||||
|
||||
func (c *EQ2Compressor) Compress(data []byte) ([]byte, error) {
|
||||
if len(data) == 0 {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
writer, err := zlib.NewWriterLevel(&buf, c.level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = writer.Write(data)
|
||||
if err != nil {
|
||||
writer.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *EQ2Compressor) Decompress(data []byte) ([]byte, error) {
|
||||
if len(data) == 0 {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
reader, err := zlib.NewReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *EQ2Compressor) ShouldCompress(data []byte) bool {
|
||||
return len(data) > 100 // EQ2 compression threshold
|
||||
}
|
202
internal/packets/protocol.go
Normal file
202
internal/packets/protocol.go
Normal file
@ -0,0 +1,202 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"eq2emu/internal/opcodes"
|
||||
)
|
||||
|
||||
type ProtocolHandler struct {
|
||||
pool *PacketPool
|
||||
}
|
||||
|
||||
func NewProtocolHandler(pool *PacketPool) *ProtocolHandler {
|
||||
return &ProtocolHandler{pool: pool}
|
||||
}
|
||||
|
||||
// Parse EQ2 protocol packet from gnet data
|
||||
func (h *ProtocolHandler) ParsePacket(data []byte) (*EQ2Packet, error) {
|
||||
if len(data) < 1 {
|
||||
return nil, fmt.Errorf("packet too small")
|
||||
}
|
||||
|
||||
packet := h.pool.GetEQ2Packet()
|
||||
packet.Opcode = opcodes.ProtocolOpcode(data[0])
|
||||
|
||||
if len(data) > 1 {
|
||||
packet.Data = make([]byte, len(data)-1)
|
||||
copy(packet.Data, data[1:])
|
||||
}
|
||||
|
||||
// Extract sequence if present
|
||||
if packet.Opcode == opcodes.OP_Packet || packet.Opcode == opcodes.OP_Combined {
|
||||
if len(packet.Data) >= 2 {
|
||||
packet.Sequence = binary.LittleEndian.Uint16(packet.Data[:2])
|
||||
}
|
||||
}
|
||||
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
// Extract application packets from protocol packet
|
||||
func (h *ProtocolHandler) ExtractAppPackets(packet *EQ2Packet, session *Session) ([]*AppPacket, error) {
|
||||
switch packet.Opcode {
|
||||
case opcodes.OP_Packet:
|
||||
return h.extractSingleApp(packet.Data)
|
||||
case opcodes.OP_Combined:
|
||||
return h.extractCombinedApps(packet.Data)
|
||||
case opcodes.OP_Fragment:
|
||||
return h.handleFragment(packet, session)
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ProtocolHandler) extractSingleApp(data []byte) ([]*AppPacket, error) {
|
||||
if len(data) < 4 { // seq(2) + opcode(2)
|
||||
return nil, fmt.Errorf("packet too small")
|
||||
}
|
||||
|
||||
offset := 2 // Skip sequence
|
||||
|
||||
// Check compression flag
|
||||
compressed := false
|
||||
if data[offset] == 0xFF {
|
||||
compressed = true
|
||||
offset++
|
||||
}
|
||||
|
||||
if len(data) < offset+2 {
|
||||
return nil, fmt.Errorf("insufficient data")
|
||||
}
|
||||
|
||||
opcode := binary.LittleEndian.Uint16(data[offset:])
|
||||
payload := data[offset+2:]
|
||||
|
||||
app := h.pool.GetAppPacket()
|
||||
app.Opcode = opcodes.AppOpcode(opcode)
|
||||
app.Data = make([]byte, len(payload))
|
||||
copy(app.Data, payload)
|
||||
app.Compressed = compressed
|
||||
|
||||
return []*AppPacket{app}, nil
|
||||
}
|
||||
|
||||
func (h *ProtocolHandler) extractCombinedApps(data []byte) ([]*AppPacket, error) {
|
||||
if len(data) < 2 {
|
||||
return nil, fmt.Errorf("combined packet too small")
|
||||
}
|
||||
|
||||
remaining := data[2:] // Skip sequence
|
||||
var apps []*AppPacket
|
||||
|
||||
for len(remaining) > 0 {
|
||||
if len(remaining) < 1 {
|
||||
break
|
||||
}
|
||||
|
||||
// Get sub-packet length
|
||||
subLen := uint16(remaining[0])
|
||||
offset := 1
|
||||
|
||||
if subLen == 0xFF {
|
||||
if len(remaining) < 3 {
|
||||
break
|
||||
}
|
||||
subLen = binary.LittleEndian.Uint16(remaining[1:3])
|
||||
offset = 3
|
||||
}
|
||||
|
||||
if len(remaining) < offset+int(subLen) {
|
||||
break
|
||||
}
|
||||
|
||||
subData := remaining[offset : offset+int(subLen)]
|
||||
remaining = remaining[offset+int(subLen):]
|
||||
|
||||
if len(subData) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
opcode := binary.LittleEndian.Uint16(subData[:2])
|
||||
payload := subData[2:]
|
||||
|
||||
app := h.pool.GetAppPacket()
|
||||
app.Opcode = opcodes.AppOpcode(opcode)
|
||||
app.Data = make([]byte, len(payload))
|
||||
copy(app.Data, payload)
|
||||
|
||||
apps = append(apps, app)
|
||||
}
|
||||
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
func (h *ProtocolHandler) handleFragment(packet *EQ2Packet, session *Session) ([]*AppPacket, error) {
|
||||
if len(packet.Data) < 6 { // seq(2) + fragInfo(2) + data
|
||||
return nil, fmt.Errorf("fragment too small")
|
||||
}
|
||||
|
||||
sequence := packet.Sequence
|
||||
fragInfo := binary.LittleEndian.Uint16(packet.Data[2:4])
|
||||
fragData := packet.Data[4:]
|
||||
|
||||
current := fragInfo & 0xFF
|
||||
total := fragInfo >> 8
|
||||
|
||||
// Get or create fragment buffer
|
||||
fragBuffer := session.Fragments[sequence]
|
||||
if fragBuffer == nil {
|
||||
fragBuffer = &FragmentBuffer{
|
||||
Fragments: make(map[uint16][]byte),
|
||||
Total: total,
|
||||
Received: 0,
|
||||
}
|
||||
session.Fragments[sequence] = fragBuffer
|
||||
}
|
||||
|
||||
// Store fragment
|
||||
fragBuffer.Fragments[current] = make([]byte, len(fragData))
|
||||
copy(fragBuffer.Fragments[current], fragData)
|
||||
fragBuffer.Received++
|
||||
|
||||
// Check if complete
|
||||
if fragBuffer.Received == fragBuffer.Total {
|
||||
// Reassemble
|
||||
var complete []byte
|
||||
for i := uint16(0); i < fragBuffer.Total; i++ {
|
||||
complete = append(complete, fragBuffer.Fragments[i]...)
|
||||
}
|
||||
|
||||
// Clean up
|
||||
delete(session.Fragments, sequence)
|
||||
|
||||
// Process as normal packet
|
||||
return h.extractSingleApp(complete)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Create protocol packets
|
||||
func (h *ProtocolHandler) CreateSessionRequest(sessionID uint32, maxLen uint32) []byte {
|
||||
data := make([]byte, 13)
|
||||
data[0] = byte(opcodes.OP_SessionRequest)
|
||||
binary.LittleEndian.PutUint32(data[1:5], sessionID)
|
||||
binary.LittleEndian.PutUint32(data[5:9], maxLen)
|
||||
return data
|
||||
}
|
||||
|
||||
func (h *ProtocolHandler) CreateSessionResponse(sessionID uint32, key uint32, format uint8) []byte {
|
||||
data := make([]byte, 10)
|
||||
data[0] = byte(opcodes.OP_SessionResponse)
|
||||
binary.LittleEndian.PutUint32(data[1:5], sessionID)
|
||||
binary.LittleEndian.PutUint32(data[5:9], key)
|
||||
data[9] = format
|
||||
return data
|
||||
}
|
||||
|
||||
func (h *ProtocolHandler) CreateKeepAlive() []byte {
|
||||
return []byte{byte(opcodes.OP_KeepAlive)}
|
||||
}
|
263
internal/packets/reader.go
Normal file
263
internal/packets/reader.go
Normal file
@ -0,0 +1,263 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// PacketReader for efficient packet parsing
|
||||
type PacketReader struct {
|
||||
data []byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func NewPacketReader(data []byte) *PacketReader {
|
||||
return &PacketReader{
|
||||
data: data,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *PacketReader) Remaining() int {
|
||||
return len(r.data) - r.offset
|
||||
}
|
||||
|
||||
func (r *PacketReader) Position() int {
|
||||
return r.offset
|
||||
}
|
||||
|
||||
func (r *PacketReader) Seek(pos int) error {
|
||||
if pos < 0 || pos > len(r.data) {
|
||||
return fmt.Errorf("seek position out of bounds")
|
||||
}
|
||||
r.offset = pos
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PacketReader) Skip(n int) error {
|
||||
if r.offset+n > len(r.data) {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
r.offset += n
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read raw bytes
|
||||
func (r *PacketReader) ReadBytes(n int) ([]byte, error) {
|
||||
if r.offset+n > len(r.data) {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
result := make([]byte, n)
|
||||
copy(result, r.data[r.offset:r.offset+n])
|
||||
r.offset += n
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadBytesRef(n int) ([]byte, error) {
|
||||
if r.offset+n > len(r.data) {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
result := r.data[r.offset : r.offset+n]
|
||||
r.offset += n
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Read integers
|
||||
func (r *PacketReader) ReadUint8() (uint8, error) {
|
||||
if r.offset >= len(r.data) {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
result := r.data[r.offset]
|
||||
r.offset++
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadInt8() (int8, error) {
|
||||
val, err := r.ReadUint8()
|
||||
return int8(val), err
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadUint16() (uint16, error) {
|
||||
if r.offset+2 > len(r.data) {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
result := binary.LittleEndian.Uint16(r.data[r.offset:])
|
||||
r.offset += 2
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadInt16() (int16, error) {
|
||||
val, err := r.ReadUint16()
|
||||
return int16(val), err
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadUint32() (uint32, error) {
|
||||
if r.offset+4 > len(r.data) {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
result := binary.LittleEndian.Uint32(r.data[r.offset:])
|
||||
r.offset += 4
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadInt32() (int32, error) {
|
||||
val, err := r.ReadUint32()
|
||||
return int32(val), err
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadUint64() (uint64, error) {
|
||||
if r.offset+8 > len(r.data) {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
result := binary.LittleEndian.Uint64(r.data[r.offset:])
|
||||
r.offset += 8
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadInt64() (int64, error) {
|
||||
val, err := r.ReadUint64()
|
||||
return int64(val), err
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadFloat32() (float32, error) {
|
||||
if r.offset+4 > len(r.data) {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
bits := binary.LittleEndian.Uint32(r.data[r.offset:])
|
||||
result := *(*float32)(unsafe.Pointer(&bits))
|
||||
r.offset += 4
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadFloat64() (float64, error) {
|
||||
if r.offset+8 > len(r.data) {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
bits := binary.LittleEndian.Uint64(r.data[r.offset:])
|
||||
result := *(*float64)(unsafe.Pointer(&bits))
|
||||
r.offset += 8
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Read strings
|
||||
func (r *PacketReader) ReadString() (string, error) {
|
||||
length, err := r.ReadUint16()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if r.offset+int(length) > len(r.data) {
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
result := string(r.data[r.offset : r.offset+int(length)])
|
||||
r.offset += int(length)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadStringFixed(length int) (string, error) {
|
||||
if r.offset+length > len(r.data) {
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// Find null terminator
|
||||
end := r.offset + length
|
||||
for i := r.offset; i < end; i++ {
|
||||
if r.data[i] == 0 {
|
||||
end = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
result := string(r.data[r.offset:end])
|
||||
r.offset += length
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadCString() (string, error) {
|
||||
start := r.offset
|
||||
for r.offset < len(r.data) && r.data[r.offset] != 0 {
|
||||
r.offset++
|
||||
}
|
||||
|
||||
if r.offset >= len(r.data) {
|
||||
return "", io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
result := string(r.data[start:r.offset])
|
||||
r.offset++ // Skip null terminator
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// EQ2-specific reads
|
||||
func (r *PacketReader) ReadEQ2String() (string, error) {
|
||||
// EQ2 strings are prefixed with length as uint16
|
||||
return r.ReadString()
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadPackedInt() (uint32, error) {
|
||||
// EQ2 packed integer format
|
||||
val, err := r.ReadUint8()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if val < 0xFF {
|
||||
return uint32(val), nil
|
||||
}
|
||||
|
||||
// Extended format
|
||||
val2, err := r.ReadUint16()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if val2 < 0xFFFF {
|
||||
return uint32(val2), nil
|
||||
}
|
||||
|
||||
// Full 32-bit
|
||||
return r.ReadUint32()
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadBool() (bool, error) {
|
||||
val, err := r.ReadUint8()
|
||||
return val != 0, err
|
||||
}
|
||||
|
||||
// Peek without advancing
|
||||
func (r *PacketReader) PeekUint8() (uint8, error) {
|
||||
if r.offset >= len(r.data) {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
return r.data[r.offset], nil
|
||||
}
|
||||
|
||||
func (r *PacketReader) PeekUint16() (uint16, error) {
|
||||
if r.offset+2 > len(r.data) {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
return binary.LittleEndian.Uint16(r.data[r.offset:]), nil
|
||||
}
|
||||
|
||||
func (r *PacketReader) PeekUint32() (uint32, error) {
|
||||
if r.offset+4 > len(r.data) {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
return binary.LittleEndian.Uint32(r.data[r.offset:]), nil
|
||||
}
|
||||
|
||||
// Buffer management
|
||||
func (r *PacketReader) Reset(data []byte) {
|
||||
r.data = data
|
||||
r.offset = 0
|
||||
}
|
||||
|
||||
func (r *PacketReader) Clone() *PacketReader {
|
||||
return &PacketReader{
|
||||
data: r.data,
|
||||
offset: r.offset,
|
||||
}
|
||||
}
|
75
internal/packets/session.go
Normal file
75
internal/packets/session.go
Normal file
@ -0,0 +1,75 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SessionManager struct {
|
||||
sessions map[uint32]*Session
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewSessionManager() *SessionManager {
|
||||
return &SessionManager{
|
||||
sessions: make(map[uint32]*Session),
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *SessionManager) GetSession(id uint32) *Session {
|
||||
sm.mu.RLock()
|
||||
defer sm.mu.RUnlock()
|
||||
return sm.sessions[id]
|
||||
}
|
||||
|
||||
func (sm *SessionManager) CreateSession(id uint32) *Session {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
|
||||
session := &Session{
|
||||
ID: id,
|
||||
Established: false,
|
||||
NextSeq: 0,
|
||||
LastKeep: time.Now(),
|
||||
Compressed: false,
|
||||
Fragments: make(map[uint16]*FragmentBuffer),
|
||||
}
|
||||
|
||||
sm.sessions[id] = session
|
||||
return session
|
||||
}
|
||||
|
||||
func (sm *SessionManager) RemoveSession(id uint32) {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
delete(sm.sessions, id)
|
||||
}
|
||||
|
||||
func (sm *SessionManager) UpdateKeepAlive(id uint32) {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
if session := sm.sessions[id]; session != nil {
|
||||
session.LastKeep = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *SessionManager) GetNextSequence(id uint32) uint16 {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
|
||||
if session := sm.sessions[id]; session != nil {
|
||||
seq := session.NextSeq
|
||||
session.NextSeq++
|
||||
return seq
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (sm *SessionManager) SetEstablished(id uint32, established bool) {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
|
||||
if session := sm.sessions[id]; session != nil {
|
||||
session.Established = established
|
||||
}
|
||||
}
|
88
internal/packets/types.go
Normal file
88
internal/packets/types.go
Normal file
@ -0,0 +1,88 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"eq2emu/internal/opcodes"
|
||||
)
|
||||
|
||||
// EQ2 protocol packet
|
||||
type EQ2Packet struct {
|
||||
Opcode opcodes.ProtocolOpcode
|
||||
Data []byte
|
||||
Sequence uint16
|
||||
}
|
||||
|
||||
// EQ2 application packet
|
||||
type AppPacket struct {
|
||||
Opcode opcodes.AppOpcode
|
||||
Data []byte
|
||||
Compressed bool
|
||||
}
|
||||
|
||||
// Session for EQ2 connection state
|
||||
type Session struct {
|
||||
ID uint32
|
||||
Established bool
|
||||
NextSeq uint16
|
||||
LastKeep time.Time
|
||||
Compressed bool
|
||||
Fragments map[uint16]*FragmentBuffer
|
||||
}
|
||||
|
||||
// Fragment reassembly
|
||||
type FragmentBuffer struct {
|
||||
Fragments map[uint16][]byte
|
||||
Total uint16
|
||||
Received uint16
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
// Packet pools
|
||||
type PacketPool struct {
|
||||
eq2Packets chan *EQ2Packet
|
||||
appPackets chan *AppPacket
|
||||
}
|
||||
|
||||
func NewPacketPool(size int) *PacketPool {
|
||||
return &PacketPool{
|
||||
eq2Packets: make(chan *EQ2Packet, size),
|
||||
appPackets: make(chan *AppPacket, size),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PacketPool) GetEQ2Packet() *EQ2Packet {
|
||||
select {
|
||||
case packet := <-p.eq2Packets:
|
||||
return packet
|
||||
default:
|
||||
return &EQ2Packet{}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PacketPool) PutEQ2Packet(packet *EQ2Packet) {
|
||||
packet.Data = packet.Data[:0]
|
||||
packet.Opcode = 0
|
||||
select {
|
||||
case p.eq2Packets <- packet:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PacketPool) GetAppPacket() *AppPacket {
|
||||
select {
|
||||
case packet := <-p.appPackets:
|
||||
return packet
|
||||
default:
|
||||
return &AppPacket{}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PacketPool) PutAppPacket(packet *AppPacket) {
|
||||
packet.Data = packet.Data[:0]
|
||||
packet.Opcode = 0
|
||||
select {
|
||||
case p.appPackets <- packet:
|
||||
default:
|
||||
}
|
||||
}
|
144
internal/packets/writer.go
Normal file
144
internal/packets/writer.go
Normal file
@ -0,0 +1,144 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type PacketWriter struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func NewPacketWriter() *PacketWriter {
|
||||
return &PacketWriter{
|
||||
data: make([]byte, 0, 512),
|
||||
}
|
||||
}
|
||||
|
||||
func NewPacketWriterSize(size int) *PacketWriter {
|
||||
return &PacketWriter{
|
||||
data: make([]byte, 0, size),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *PacketWriter) Bytes() []byte {
|
||||
return w.data
|
||||
}
|
||||
|
||||
func (w *PacketWriter) Len() int {
|
||||
return len(w.data)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) Reset() {
|
||||
w.data = w.data[:0]
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteBytes(data []byte) {
|
||||
w.data = append(w.data, data...)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteUint8(val uint8) {
|
||||
w.data = append(w.data, val)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteInt8(val int8) {
|
||||
w.WriteUint8(uint8(val))
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteUint16(val uint16) {
|
||||
b := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(b, val)
|
||||
w.data = append(w.data, b...)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteInt16(val int16) {
|
||||
w.WriteUint16(uint16(val))
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteUint32(val uint32) {
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, val)
|
||||
w.data = append(w.data, b...)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteInt32(val int32) {
|
||||
w.WriteUint32(uint32(val))
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteUint64(val uint64) {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, val)
|
||||
w.data = append(w.data, b...)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteInt64(val int64) {
|
||||
w.WriteUint64(uint64(val))
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteFloat32(val float32) {
|
||||
bits := *(*uint32)(unsafe.Pointer(&val))
|
||||
w.WriteUint32(bits)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteFloat64(val float64) {
|
||||
bits := *(*uint64)(unsafe.Pointer(&val))
|
||||
w.WriteUint64(bits)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteString(s string) {
|
||||
w.WriteUint16(uint16(len(s)))
|
||||
w.data = append(w.data, []byte(s)...)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteStringFixed(s string, length int) {
|
||||
data := make([]byte, length)
|
||||
copy(data, []byte(s))
|
||||
w.data = append(w.data, data...)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteCString(s string) {
|
||||
w.data = append(w.data, []byte(s)...)
|
||||
w.data = append(w.data, 0)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteEQ2String(s string) {
|
||||
w.WriteString(s)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WritePackedInt(val uint32) {
|
||||
if val < 0xFF {
|
||||
w.WriteUint8(uint8(val))
|
||||
} else if val < 0xFFFF {
|
||||
w.WriteUint8(0xFF)
|
||||
w.WriteUint16(uint16(val))
|
||||
} else {
|
||||
w.WriteUint8(0xFF)
|
||||
w.WriteUint16(0xFFFF)
|
||||
w.WriteUint32(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteBool(val bool) {
|
||||
if val {
|
||||
w.WriteUint8(1)
|
||||
} else {
|
||||
w.WriteUint8(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteZeros(count int) {
|
||||
zeros := make([]byte, count)
|
||||
w.data = append(w.data, zeros...)
|
||||
}
|
||||
|
||||
func (w *PacketWriter) SetUint16At(pos int, val uint16) {
|
||||
if pos+1 < len(w.data) {
|
||||
binary.LittleEndian.PutUint16(w.data[pos:], val)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *PacketWriter) SetUint32At(pos int, val uint32) {
|
||||
if pos+3 < len(w.data) {
|
||||
binary.LittleEndian.PutUint32(w.data[pos:], val)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user