func P_forward_req(hostid int32, pkt *packet.Packet) []byte { tbl, _ := PKT_FORWARDIPC(pkt) object := &IPCObject{} err := json.Unmarshal(tbl.F_IPC, object) if err != nil { log.Println("decode forward IPCObject error") return nil } // if user is online, send to the server, or else send to database state := core.State(object.DestID) //fmt.Println(tbl.F_dest_id, tbl.F_IPC) switch state { case core.ON_PROT, core.ON_FREE: host := core.Host(object.DestID) ch := ForwardChan(host) if ch != nil { ch <- tbl.F_IPC } else { forward_tbl.Push(object) } default: forward_tbl.Push(object) } ret := INT{F_v: 1} return packet.Pack(-1, &ret, nil) }
//---------------------------------------------------------- p2p send from src_id to dest_id func Send(src_id, dest_id int32, service int16, multicast bool, object interface{}) (ret bool) { if multicast { return _multicast(src_id, dest_id, service, object) } // convert the OBJECT to json, LEVEL-1 encapsulation val, err := json.Marshal(object) if err != nil { log.Println("cannot marshal object to json", err) return false } req := &IPCObject{SrcID: src_id, DestID: dest_id, Service: service, Object: val, Time: time.Now().Unix()} // first try local delivery, if dest_id is not in the same server, just forward to HUB server. peer := gsdb.QueryOnline(dest_id) if peer != nil { defer func() { if x := recover(); x != nil { ret = false forward_tbl.Push(req) } }() select { case peer.MQ <- *req: case <-time.After(time.Second): panic("deadlock") // rare case, when both chan is full } return true } else { // convert req to json again, LEVEL-2 encapsulation return hub_client.Forward(req) } }
//---------------------------------------------------------- deliver an IPCObject to a user func _deliver(obj *IPCObject) { sess := gsdb.QueryOnline(obj.DestID) if sess != nil { func() { defer func() { if x := recover(); x != nil { forward_tbl.Push(obj) } }() sess.MQ <- *obj }() } else { forward_tbl.Push(obj) } }
func _unicast(hostid int32, obj *IPCObject) { // if user is online, send to the server, or else send to database state := core.State(obj.DestID) switch state { case ON_PROT, ON_FREE: host := core.Host(obj.DestID) ch := ForwardChan(host) if ch != nil { ch <- *obj } else { forward_tbl.Push(obj) } default: forward_tbl.Push(obj) } }
//------------------------------------------------ 载入离线时收到的的IPCObject func LoadIPCObjects(user_id int32, MQ chan IPCObject) { objs := forward_tbl.PopAll(user_id) // 消息没有完全push到MQ, 存回db var k int defer func() { if x := recover(); x != nil { for k < len(objs) { forward_tbl.Push(&objs[k]) k++ } } }() for k = range objs { MQ <- objs[k] } }
//----------------------------------------------- connection close cleanup work func close_work(sess *Session) { if sess.LoggedIn { hub_client.Logout(sess.User.Id) gsdb.UnregisterOnline(sess.User.Id) close(sess.MQ) // 未处理的IPC数据,重新放入db if len(sess.MQ) > 0 { log.Println("re-push ipcobject back to db") } for len(sess.MQ) > 0 { ipcobject := <-sess.MQ forward_tbl.Push(&ipcobject) } // 持久化逻辑#3: 离线时,刷入数据库 _flush(sess) } }
//---------------------------------------------------------- 异步消息发送 func Send(src_id, dest_id int32, service int16, object interface{}) (ret bool) { // 况序列化被传输对象为json val, err := json.Marshal(object) if err != nil { ERR("cannot marshal object to json", err) return false } // Send函数不能投递到SYS_USR if dest_id == SYS_USR { ERR("cannot Send to SYS_USR") return false } // 打包为IPCObject req := &IPCObject{SrcID: src_id, DestID: dest_id, Service: service, Object: val, Time: time.Now().Unix()} peer := gsdb.QueryOnline(dest_id) if peer != nil { // 如果玩家在本服务器 // 对方的channel 可能会close, 需要处理panic的情况 defer func() { if x := recover(); x != nil { ret = false forward_tbl.Push(req) } }() select { case peer.MQ <- *req: case <-time.After(time.Second): panic("deadlock") // rare case, when both chans are full. } return true } else { // 通过HUB转发IPCObject return hub_client.Forward(req) } return false }
//----------------------------------------------- cleanup work after disconnection func close_work(sess *Session) { defer PrintPanicStack() if sess.Flag&SESS_LOGGED_IN == 0 { return } // must flush user data _flush(sess) // notify hub hub_client.Logout(sess.User.Id) // unregister online at this server gsdb.UnregisterOnline(sess.User.Id) // close MQ, and save the queue to db close(sess.MQ) for ipcobject := range sess.MQ { forward_tbl.Push(&ipcobject) NOTICE("re-pushed ipcobject back to db, userid:", sess.User.Id) } NOTICE(sess.User.Name, "disconnected from", sess.IP, "country:", geoip.Query(sess.IP)) }
//----------------------------------------------- receive message from hub func HubReceiver(conn net.Conn) { defer conn.Close() header := make([]byte, 2) seq_id := make([]byte, 8) for { // header n, err := io.ReadFull(conn, header) if n == 0 && err == io.EOF { break } else if err != nil { log.Println("error receiving header:", err) break } // packet seq_id uint32 n, err = io.ReadFull(conn, seq_id) if n == 0 && err == io.EOF { break } else if err != nil { log.Println("error receiving seq_id:", err) break } // read big-endian header seqval := binary.BigEndian.Uint64(seq_id) size := binary.BigEndian.Uint16(header) - 8 data := make([]byte, size) n, err = io.ReadFull(conn, data) if err != nil { log.Println("error receiving msg:", err) break } // two kinds of IPC: // a). Hub Sends to GS, sequence number is not required (set to 0), just forwarding to session // b). Call, sequence number is needed, send will wake up blocking-chan. // if seqval == 0 { obj := &IPCObject{} err := json.Unmarshal(data, obj) if err != nil { log.Println("unable to decode received IPCObject") continue } sess := gsdb.QueryOnline(obj.DestID) if sess == nil { // if the user is disconnected forward_tbl.Push(obj) } else { func() { defer func() { if x := recover(); x != nil { log.Println("forward: deliver to MQ failed.") forward_tbl.Push(obj) } }() sess.MQ <- *obj }() } } else { _wait_ack_lock.Lock() if ack, ok := _wait_ack[seqval]; ok { ack <- data delete(_wait_ack, seqval) } else { log.Printf("Illegal packet sequence number [%x] from HUB", seqval) } _wait_ack_lock.Unlock() } } }