1
0
Protocol/stream/factory.go
2025-09-03 13:29:04 -05:00

272 lines
6.1 KiB
Go

package stream
import (
"database/sql"
"log"
"sync"
"time"
"git.sharkk.net/EQ2/Protocol/opcodes"
"git.sharkk.net/EQ2/Protocol/packets"
"github.com/panjf2000/gnet/v2"
)
// StreamFactory manages EQStream instances using gnet
type StreamFactory struct {
gnet.BuiltinEventEngine
mu sync.RWMutex
streams map[gnet.Conn]*Stream
// Configuration
crcKey uint32
maxPacketSize uint16
encodeKey int
decodeKey int
opcodeSize uint8
clientVersion int16
// Database for opcodes
db *sql.DB
opcodeTable string
opcodeManager opcodes.Manager
// Callbacks
onNewStream func(*Stream)
onStreamClosed func(*Stream)
onPacket func(*Stream, *packets.AppPacket)
// Stats
totalStreams uint64
activeStreams uint64
}
// FactoryConfig holds factory configuration
type FactoryConfig struct {
CRCKey uint32
MaxPacketSize uint16
EncodeKey int
DecodeKey int
OpcodeSize uint8
ClientVersion int16
Database *sql.DB
OpcodeTable string
}
// NewStreamFactory creates a new factory
func NewStreamFactory(cfg *FactoryConfig) *StreamFactory {
f := &StreamFactory{
streams: make(map[gnet.Conn]*Stream),
crcKey: cfg.CRCKey,
maxPacketSize: cfg.MaxPacketSize,
encodeKey: cfg.EncodeKey,
decodeKey: cfg.DecodeKey,
opcodeSize: cfg.OpcodeSize,
clientVersion: cfg.ClientVersion,
db: cfg.Database,
opcodeTable: cfg.OpcodeTable,
}
if f.maxPacketSize == 0 {
f.maxPacketSize = packets.DefaultMTU
}
if f.opcodeSize == 0 {
f.opcodeSize = packets.DefaultOpcodeSize
}
if f.opcodeTable == "" {
f.opcodeTable = "opcodes"
}
// Initialize opcode manager
if cfg.ClientVersion > 0 && cfg.Database != nil {
f.opcodeManager = opcodes.GetManager(cfg.ClientVersion, cfg.Database, f.opcodeTable)
}
return f
}
// OnBoot called when server starts
func (f *StreamFactory) OnBoot(eng gnet.Engine) gnet.Action {
log.Printf("StreamFactory started")
return gnet.None
}
// OnOpen called when new connection opens
func (f *StreamFactory) OnOpen(c gnet.Conn) ([]byte, gnet.Action) {
// Create stream configuration
streamCfg := &Config{
SessionID: uint32(time.Now().UnixNano()),
CRCKey: f.crcKey,
MaxPacketSize: f.maxPacketSize,
EncodeKey: f.encodeKey,
DecodeKey: f.decodeKey,
OpcodeManager: f.opcodeManager,
OpcodeSize: f.opcodeSize,
AckThreshold: 200 * time.Millisecond,
KeepaliveTime: 10 * time.Second,
TimeoutDuration: 30 * time.Second,
}
// Create new stream
stream := NewStream(c, streamCfg)
// Set packet callback
stream.SetPacketCallback(func(app *packets.AppPacket) {
if f.onPacket != nil {
f.onPacket(stream, app)
}
})
// Store stream
f.mu.Lock()
f.streams[c] = stream
f.activeStreams++
f.totalStreams++
f.mu.Unlock()
// Notify callback
if f.onNewStream != nil {
f.onNewStream(stream)
}
c.SetContext(stream)
log.Printf("New stream from %s", c.RemoteAddr())
return nil, gnet.None
}
// OnClose called when connection closes
func (f *StreamFactory) OnClose(c gnet.Conn, err error) gnet.Action {
f.mu.Lock()
stream, exists := f.streams[c]
if exists {
delete(f.streams, c)
f.activeStreams--
}
f.mu.Unlock()
if stream != nil {
stream.Close()
// Notify callback
if f.onStreamClosed != nil {
f.onStreamClosed(stream)
}
log.Printf("Stream closed: %s", c.RemoteAddr())
}
return gnet.None
}
// OnTraffic handles incoming data
func (f *StreamFactory) OnTraffic(c gnet.Conn) gnet.Action {
stream := c.Context().(*Stream)
if stream == nil {
return gnet.Close
}
// Read all available data
buf, err := c.Next(-1)
if err != nil {
return gnet.Close
}
// Process packet
if err := stream.Process(buf); err != nil {
log.Printf("Stream process error: %v", err)
return gnet.Close
}
return gnet.None
}
// OnTick called periodically to process stream queues
func (f *StreamFactory) OnTick() (time.Duration, gnet.Action) {
f.mu.RLock()
streams := make([]*Stream, 0, len(f.streams))
for _, stream := range f.streams {
streams = append(streams, stream)
}
f.mu.RUnlock()
// Process queues for all streams
for _, stream := range streams {
stream.processQueues()
}
return 100 * time.Millisecond, gnet.None
}
// GetStream returns stream for a connection
func (f *StreamFactory) GetStream(c gnet.Conn) *Stream {
f.mu.RLock()
defer f.mu.RUnlock()
return f.streams[c]
}
// GetAllStreams returns all active streams
func (f *StreamFactory) GetAllStreams() []*Stream {
f.mu.RLock()
defer f.mu.RUnlock()
result := make([]*Stream, 0, len(f.streams))
for _, stream := range f.streams {
result = append(result, stream)
}
return result
}
// SetNewStreamCallback sets callback for new streams
func (f *StreamFactory) SetNewStreamCallback(fn func(*Stream)) {
f.onNewStream = fn
}
// SetStreamClosedCallback sets callback for closed streams
func (f *StreamFactory) SetStreamClosedCallback(fn func(*Stream)) {
f.onStreamClosed = fn
}
// SetPacketCallback sets global packet callback
func (f *StreamFactory) SetPacketCallback(fn func(*Stream, *packets.AppPacket)) {
f.onPacket = fn
}
// UpdateOpcodeManager updates opcodes for a specific version
func (f *StreamFactory) UpdateOpcodeManager(version int16) {
if f.db != nil {
f.opcodeManager = opcodes.GetManager(version, f.db, f.opcodeTable)
log.Printf("Updated opcode manager to version %d", version)
}
}
// Stats returns factory statistics
func (f *StreamFactory) Stats() map[string]uint64 {
f.mu.RLock()
defer f.mu.RUnlock()
return map[string]uint64{
"total_streams": f.totalStreams,
"active_streams": f.activeStreams,
}
}
// Run starts the factory as a UDP server
func Run(addr string, factory *StreamFactory) error {
return gnet.Run(factory, addr,
gnet.WithMulticore(true),
gnet.WithReuseAddr(true),
gnet.WithReusePort(true),
gnet.WithTicker(true),
gnet.WithTCPKeepAlive(0), // Disable for UDP
gnet.WithSocketRecvBuffer(1024*1024),
gnet.WithSocketSendBuffer(1024*1024),
)
}
// RunTLS starts the factory with TLS (future enhancement)
func RunTLS(addr string, factory *StreamFactory, certFile, keyFile string) error {
// TLS configuration would go here if needed
return Run(addr, factory)
}