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) }