func Protect(id int32, until int64) bool { _lock_players.Lock() player := _players[id] _lock_players.Unlock() if player != nil { player.Lock() defer player.Unlock() switch player.State { case ON_FREE: player.State = ON_PROT player.ProtectTimeout = until timer.Add(id, until, _waits_ch) case OFF_RAID: player.State = OFF_PROT player.ProtectTimeout = until timer.Add(id, until, _waits_ch) case ON_PROT: // protect + protect player.ProtectTimeout = until timer.Add(id, until, _waits_ch) default: return false } return true } return false }
//----------------------------------------------- Start Agent when a client is connected func StartAgent(sess *Session, in chan []byte, out *Buffer) { wg.Add(1) defer wg.Done() defer helper.PrintPanicStack() // init session sess.MQ = make(chan IPCObject, DEFAULT_MQ_SIZE) sess.ConnectTime = time.Now() sess.LastPacketTime = time.Now() // custom-sec timer, 60-sec custom_timer := make(chan int32, 1) timer.Add(-1, time.Now().Unix()+CUSTOM_TIMER, custom_timer) // cleanup work defer func() { close_work(sess) }() // the main message loop for { select { case msg, ok := <-in: // network protocol if !ok { return } sess.PacketTime = time.Now() if result := UserRequestProxy(sess, msg); result != nil { err := out.Send(result) if err != nil { helper.ERR("cannot send to client", err) return } } sess.LastPacketTime = sess.PacketTime sess.PacketCount++ // packet count case msg := <-sess.MQ: // internal IPC if result := IPCRequestProxy(sess, &msg); result != nil { err := out.Send(result) if err != nil { helper.ERR("cannot send ipc response", err) return } } case <-custom_timer: // 60-sec timer timer_work(sess) timer.Add(-1, time.Now().Unix()+CUSTOM_TIMER, custom_timer) case <-die: sess.Flag |= SESS_KICKED_OUT } // is the session been kicked out if sess.Flag&SESS_KICKED_OUT != 0 { return } } }
func Protect(id int32, until int64) bool { _lock_players.RLock() player := _players[id] _lock_players.RUnlock() if player != nil { player.Lock() defer player.Unlock() switch player.State { case ON_FREE: event_id := atomic.AddInt32(&_event_id_gen, 1) timer.Add(event_id, until, _waits_ch) player.State = ON_PROT player.ProtectTimeout = until player.WaitEventId = event_id _waits_lock.Lock() _waits[event_id] = player _waits_lock.Unlock() case OFF_RAID: event_id := atomic.AddInt32(&_event_id_gen, 1) timer.Add(event_id, until, _waits_ch) player.State = OFF_PROT player.ProtectTimeout = until player.WaitEventId = event_id _waits_lock.Lock() _waits[event_id] = player _waits_lock.Unlock() case ON_PROT: event_id := atomic.AddInt32(&_event_id_gen, 1) timer.Add(event_id, until, _waits_ch) player.State = ON_PROT player.ProtectTimeout = until player.WaitEventId = event_id _waits_lock.Lock() _waits[event_id] = player _waits_lock.Unlock() default: return false } return true } return false }
//---------------------------------------------------------- system routine func SysRoutine() { var sess Session sess.MQ = make(chan IPCObject, SYS_MQ_SIZE) gsdb.RegisterOnline(&sess, SYS_USR) // timer gc_timer := make(chan int32, 10) gc_timer <- 1 for { select { case msg, ok := <-sess.MQ: // IPCObject to system routine if !ok { return } IPCRequestProxy(&sess, &msg) case <-gc_timer: runtime.GC() INFO("== PERFORMANCE LOG ==") INFO("Goroutine Count:", runtime.NumGoroutine()) INFO("GC Summary:", GCSummary()) INFO("Sysroutine MQ size:", len(sess.MQ)) timer.Add(0, time.Now().Unix()+GC_INTERVAL, gc_timer) } } }
//---------------------------------------------------------- 系统routine func SysRoutine() { // timer gc_timer := make(chan int32, 1) timer.Add(0, time.Now().Unix()+GC_INTERVAL, gc_timer) for { select { case <-gc_timer: runtime.GC() log.Println("GC executed") log.Println("NumGoroutine", runtime.NumGoroutine()) log.Println("GC Summary:", helper.GCSummary()) timer.Add(0, time.Now().Unix()+GC_INTERVAL, gc_timer) } } }
//---------------------------------------------------------- Load a timeout event at startup func Load(event_id int32, Type int16, user_id int32, timeout int64, params []byte) { timer.Add(event_id, timeout, _event_ch) event := &Event{EventId: event_id, Type: Type, UserId: user_id, Timeout: timeout, Params: params} _events[event_id] = event return }
func Raid(id int32) bool { _lock_players.RLock() player := _players[id] _lock_players.RUnlock() if player != nil { player.Lock() defer player.Unlock() switch player.State { case OFF_FREE: timeout := time.Now().Unix() + RAID_TIME event_id := atomic.AddInt32(&_event_id_gen, 1) timer.Add(event_id, timeout, _waits_ch) // generate timer player.State = OFF_RAID player.RaidTimeout = timeout player.WaitEventId = event_id _waits_lock.Lock() _waits[event_id] = player _waits_lock.Unlock() default: return false } return true } return false }
func stats_sender() { _accum_buffer := make(map[string]map[string]int32) _update_buffer := make(map[string]map[string]string) stats_timer := make(chan int32, 100) stats_timer <- 1 for { select { case req := <-AccumQueue: if _, ok := _accum_buffer[req.F_lang]; !ok { val := make(map[string]int32) val[req.F_key] = 0 _accum_buffer[req.F_lang] = val } val := _accum_buffer[req.F_lang] val[req.F_key] += req.F_value _accum_buffer[req.F_lang] = val case req := <-UpdateQueue: if _, ok := _update_buffer[req.F_lang]; !ok { val := make(map[string]string) val[req.F_key] = "" _update_buffer[req.F_lang] = val } val := _update_buffer[req.F_lang] val[req.F_key] = req.F_value _update_buffer[req.F_lang] = val case <-stats_timer: INFO("Stats Buffer:", len(_accum_buffer), len(_update_buffer)) // 累计 accum := SET_ADDS_REQ{} for accum.F_lang, _ = range _accum_buffer { for accum.F_key, accum.F_value = range _accum_buffer[accum.F_lang] { Send(packet.Pack(Code["set_adds_req"], &accum, nil)) } } _accum_buffer = make(map[string]map[string]int32) // 更新 update := SET_UPDATE_REQ{} for update.F_lang, _ = range _update_buffer { for update.F_key, update.F_value = range _update_buffer[update.F_lang] { Send(packet.Pack(Code["set_update_req"], &update, nil)) } } _update_buffer = make(map[string]map[string]string) // FINI config := cfg.Get() period := STATS_COLLECT_PERIOD if config["stats_collect_period"] != "" { period, _ = strconv.Atoi(config["stats_collect_period"]) } timer.Add(0, time.Now().Unix()+int64(period), stats_timer) runtime.GC() } } }
func init() { _all = make(map[int32]*Collector) CH = make(chan int32, 5) go _writer() config := cfg.Get() trigger, err := strconv.Atoi(config["collect_time"]) if err != nil { log.Println("cannot parse collect_time from config", err) } // 寻找最近的触发点 now := time.Now().Unix() passed := now % DAY_SEC if passed < int64(trigger) { timer.Add(-1, now-passed+int64(trigger), CH) } else { timer.Add(-1, now-passed+int64(trigger)+DAY_SEC, CH) } }
//---------------------------------------------------------- Add a timeout event func Add(Type int16, user_id int32, timeout int64, params []byte) int32 { event_id := db.NextVal(EVENTID_GEN) timer.Add(event_id, timeout, _event_ch) event := &Event{Type: Type, UserId: user_id, EventId: event_id, Timeout: timeout, Params: params} _events_lock.Lock() _events[event_id] = event _events_lock.Unlock() // store to db event_tbl.Add(event) return event_id }
//------------------------------------------------ Stats Server Agent func StatsAgent(incoming chan []byte, conn net.Conn) { queue_timer := make(chan int32, 1) queue_timer <- 1 for { select { case sample := <-incoming: reader := packet.Reader(sample) HandleRequest(reader) case <-queue_timer: log.Println("============== STATS QUEUE SIZE:", len(incoming), "===================") timer.Add(1, time.Now().Unix()+PRINT_INTERVAL, queue_timer) } } }
//------------------------------------------------ add a user to finite state machine manager func _add_fsm(user *User) { player := &PlayerInfo{Id: user.Id} player.ProtectTimeout = user.ProtectTimeout if user.ProtectTimeout > time.Now().Unix() { // 有保护时间 player.State = OFF_PROT _lock_players.Lock() _players[player.Id] = player _lock_players.Unlock() timer.Add(user.Id, user.ProtectTimeout, _waits_ch) } else { player.State = OFF_FREE _lock_players.Lock() _players[user.Id] = player _lock_players.Unlock() } }
func Raid(id int32) bool { _lock_players.Lock() player := _players[id] _lock_players.Unlock() if player != nil { player.Lock() defer player.Unlock() switch player.State { case OFF_FREE: player.State = OFF_RAID player.RaidStart = time.Now().Unix() timeout := time.Now().Unix() + AUTO_EXPIRE // automatic expire when in raid timer.Add(player.Id, timeout, _waits_ch) default: return false } return true } return false }
//------------------------------------------------ 统计数据定时汇总写入 func _writer() { for { // 时钟信号 <-CH // 复制map已进行费事操作,不阻塞collect _all_lock.Lock() snapshot := make(map[int32]*Collector) for k, v := range _all { snapshot[k] = v } _all_lock.Unlock() // 创建每个玩家的报表 c := StatsCollection() for userid, collector := range snapshot { if collector != nil { archive := _archive(userid, collector) c.Upsert(bson.M{"userid": userid}, archive) } } now := time.Now().Unix() log.Printf("stats flush finished at %v\n", now) // 明天同一时刻再见 passed := now % DAY_SEC config := cfg.Get() trigger, err := strconv.Atoi(config["collect_time"]) if err != nil { log.Println("cannot parse collect_time from config", err) } timer.Add(-1, now-passed+int64(trigger)+DAY_SEC, CH) snapshot = nil } }
//----------------------------------------------- Start Agent when a client is connected func StartAgent(in chan []byte, conn net.Conn) { defer helper.PrintPanicStack() config := cfg.Get() if config["profile"] == "true" { helper.SetMemProfileRate(1) defer func() { helper.GC() helper.DumpHeap() helper.PrintGCSummary() }() } var sess Session sess.IP = net.ParseIP(conn.RemoteAddr().String()) sess.MQ = make(chan IPCObject, DEFAULT_MQ_SIZE) sess.ConnectTime = time.Now() sess.LastPacketTime = time.Now().Unix() sess.KickOut = false // standard 1-sec timer std_timer := make(chan int32, 1) timer.Add(1, time.Now().Unix()+1, std_timer) // write buffer bufctrl := make(chan bool) buf := NewBuffer(&sess, conn, bufctrl) go buf.Start() // max # of operartions allowed before flushing flush_ops, err := strconv.Atoi(config["flush_ops"]) if err != nil { log.Println("cannot parse flush_ops from config", err) flush_ops = DEFAULT_FLUSH_OPS } // cleanup work defer func() { close_work(&sess) close(bufctrl) }() // the main message loop for { select { case msg, ok := <-in: if !ok { return } if result := UserRequestProxy(&sess, msg); result != nil { err := buf.Send(result) if err != nil { return } } sess.LastPacketTime = time.Now().Unix() case msg, ok := <-sess.MQ: // async if !ok { return } if result := IPCRequestProxy(&sess, &msg); result != nil { err := buf.Send(result) if err != nil { return } } case <-std_timer: timer_work(&sess) if session_timeout(&sess) { return } timer.Add(1, time.Now().Unix()+1, std_timer) } // 持久化逻辑#1: 超过一定的操作数量,刷入数据库 if sess.OpCount > flush_ops { _flush(&sess) } // 是否被逻辑踢出 if sess.KickOut { return } } }