272 lines
6.1 KiB
Go
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)
|
|
}
|