func (s *server) init() { s.dirty_words = make(map[string]bool) dict_path, dirty_words_path := s.data_path() // 载入字典 log.Trace("Loading Dictionary...") s.segmenter.LoadDictionary(dict_path) log.Trace("Dictionary Loaded") // 读取脏词库 log.Trace(SERVICE, "Loading Dirty Words...") f, err := os.Open(dirty_words_path) if err != nil { log.Critical(err) os.Exit(-1) } defer f.Close() // 逐行扫描 scanner := bufio.NewScanner(f) scanner.Split(bufio.ScanLines) for scanner.Scan() { word := strings.ToUpper(strings.TrimSpace(scanner.Text())) // 均处理为大写 if word != "" { s.dirty_words[word] = true } } log.Trace("Dirty Words Loaded") }
func (s *server) init() { // 载入IP表 log.Trace("Loading GEOIP City...") reader, err := maxminddb.Open(s.data_path()) if err != nil { log.Critical(err) os.Exit(-1) } s.mmdb = reader log.Trace("GEOIP City Load Complete.") }
//---------------------------------------------------------- 读取GridFS文件 func LoadFile(filename string) (ok bool, content []byte) { ms := _global_ms.Copy() defer ms.Close() buf := &bytes.Buffer{} file, err := ms.DB("").GridFS("fs").Open(filename) if err != nil { log.Warning("gridfs", filename, err) return false, nil } n, err := io.Copy(buf, file) if err != nil { log.Error("gridfs", filename, n, err) return false, nil } err = file.Close() if err != nil { log.Error("gridfs", filename, err) return false, nil } log.Trace("gridfs", filename, "load from GridFS!!") return true, buf.Bytes() }
func (arch *Archiver) init() { arch.pending = make(chan []byte) arch.stop = make(chan bool) cfg := nsq.NewConfig() consumer, err := nsq.NewConsumer(TOPIC, CHANNEL, cfg) if err != nil { log.Critical(err) os.Exit(-1) } // message process consumer.AddHandler(nsq.HandlerFunc(func(msg *nsq.Message) error { arch.pending <- msg.Body return nil })) // read environtment variable addresses := []string{DEFAULT_NSQLOOKUPD} if env := os.Getenv(ENV_NSQLOOKUPD); env != "" { addresses = strings.Split(env, ";") } // connect to nsqlookupd log.Trace("connect to nsqlookupds ip:", addresses) if err := consumer.ConnectToNSQLookupds(addresses); err != nil { log.Critical(err) return } log.Info("nsqlookupd connected") go arch.archive_task() }
// 玩家登陆过程 func P_user_login_req(sess *Session, reader *packet.Packet) []byte { // TODO: 登陆鉴权 sess.UserId = 1 // TODO: 选择登陆服务器 sess.GSID = DEFAULT_GSID // 选服 cli, err := sp.GetServiceWithId(sp.SERVICE_GAME, sess.GSID) if err != nil { log.Critical(err) return nil } // type assertion game_cli, ok := cli.(spp.GameServiceClient) if !ok { log.Critical("cannot do type assertion on: %v", sess.GSID) return nil } // 开启到游戏服的流 // TODO: 处理context,设定超时 stream, err := game_cli.Stream(context.Background()) if err != nil { log.Critical(err) return nil } sess.Stream = stream // 在game注册 // TODO: 新用户的创建由game处理 sess.Stream.Send(&spp.Game_Frame{Type: spp.Game_Register, UserId: sess.UserId}) // 读取GAME返回消息 fetcher_task := func(sess *Session) { for { in, err := sess.Stream.Recv() if err == io.EOF { // 流关闭 log.Trace(err) return } if err != nil { log.Error(err) return } sess.MQ <- *in } } go fetcher_task(sess) return packet.Pack(Code["user_login_ack"], user_snapshot{F_uid: sess.UserId}, nil) }
// 玩家登陆过程 func P_user_login_req(sess *Session, reader *packet.Packet) []byte { // TODO: 登陆鉴权 sess.UserId = 1 // TODO: 选择登陆服务器 sess.GSID = DEFAULT_GSID // 选服 conn := sp.GetServiceWithId(sp.DEFAULT_SERVICE_PATH+"/game", sess.GSID) if conn == nil { log.Critical("cannot get game service:", sess.GSID) return nil } cli := pb.NewGameServiceClient(conn) // 开启到游戏服的流 // TODO: 处理context,设定超时 stream, err := cli.Stream(context.Background()) if err != nil { log.Critical(err) return nil } sess.Stream = stream // 在game注册 // TODO: 新用户的创建由game处理 sess.Stream.Send(&pb.Game_Frame{Type: pb.Game_Register, UserId: sess.UserId}) // 读取GAME返回消息 fetcher_task := func(sess *Session) { for { in, err := sess.Stream.Recv() if err == io.EOF { // 流关闭 log.Trace(err) return } if err != nil { log.Error(err) return } select { case sess.MQ <- *in: case <-sess.Die: } } } go fetcher_task(sess) return packet.Pack(Code["user_login_succeed_ack"], S_user_snapshot{F_uid: sess.UserId}, nil) }
//---------------------------------------------------------- 删除GridFS文件 func RemoveFile(filename string) bool { ms := _global_ms.Copy() defer ms.Close() gridfs := ms.DB("").GridFS("fs") // 删除同名文件 err := gridfs.Remove(filename) if err != nil { log.Warning("gridfs", filename, err) return false } log.Trace("gridfs", filename, "removed from GridFS!!") return true }
// 玩家登陆过程 func P_user_login_req(sess *Session, reader *packet.Packet) []byte { // TODO: 登陆鉴权 // 简单鉴权可以在agent直接完成,通常公司都存在一个用户中心服务器用于鉴权 sess.UserId = 1 // TODO: 选择GAME服务器 // 选服策略依据业务进行,比如小服可以固定选取某台,大服可以采用HASH或一致性HASH sess.GSID = DEFAULT_GSID // 连接到已选定GAME服务器 conn := sp.GetServiceWithId(sp.DEFAULT_SERVICE_PATH+"/game", sess.GSID) if conn == nil { log.Critical("cannot get game service:", sess.GSID) return nil } cli := pb.NewGameServiceClient(conn) // 开启到游戏服的流 ctx := metadata.NewContext(context.Background(), metadata.New(map[string]string{"userid": fmt.Sprint(sess.UserId)})) stream, err := cli.Stream(ctx) if err != nil { log.Critical(err) return nil } sess.Stream = stream // 读取GAME返回消息的goroutine fetcher_task := func(sess *Session) { for { in, err := sess.Stream.Recv() if err == io.EOF { // 流关闭 log.Trace(err) return } if err != nil { log.Error(err) return } select { case sess.MQ <- *in: case <-sess.Die: } } } go fetcher_task(sess) return packet.Pack(Code["user_login_succeed_ack"], S_user_snapshot{F_uid: sess.UserId}, nil) }
// stream server func (s *server) Stream(stream GameService_StreamServer) error { var sess Session ch_agent := s.recv(stream) ch_ipc := make(chan *Game_Frame, DEFAULT_CH_IPC_SIZE) defer func() { if sess.Flag&SESS_REGISTERED != 0 { // TODO: destroy session sess.Flag &^= SESS_REGISTERED registry.Unregister(sess.UserId) } log.Trace("stream end:", sess.UserId) }() // >> main message loop << for { select { case frame, ok := <-ch_agent: // frames from agent if !ok { // EOF return nil } switch frame.Type { case Game_Message: // validation if sess.Flag&SESS_REGISTERED == 0 { log.Critical("user not registered") return ERROR_USER_NOT_REGISTERED } // locate handler by proto number reader := packet.Reader(frame.Message) c, err := reader.ReadS16() if err != nil { log.Critical(err) return err } handle := client_handler.Handlers[c] if handle == nil { log.Criticalf("service not bind: %v", c) return ERROR_SERVICE_NOT_BIND } // serialized processing, no future locks needed. // multiple agents can connect simutaneously to games var ret []byte wrap := func() { ret = handle(&sess, reader) } s.latch(wrap) // construct frame & return message from logic if ret != nil { if err := stream.Send(&Game_Frame{Type: Game_Message, Message: ret}); err != nil { log.Critical(err) return err } } // session control by logic if sess.Flag&SESS_KICKED_OUT != 0 { // logic kick out if err := stream.Send(&Game_Frame{Type: Game_Kick}); err != nil { log.Critical(err) return err } return nil } case Game_Register: if sess.Flag&SESS_REGISTERED == 0 { // TODO: create session sess.Flag |= SESS_REGISTERED sess.UserId = frame.UserId registry.Register(frame.UserId, ch_ipc) log.Trace("user registered") } else { log.Critical("user already registered") } case Game_Unregister: if sess.Flag&SESS_REGISTERED != 0 { // TODO: destroy session sess.Flag &^= SESS_REGISTERED registry.Unregister(sess.UserId) log.Trace("user unregistered") } else { log.Critical("user not registered") } case Game_Ping: if err := stream.Send(&Game_Frame{Type: Game_Ping, Message: frame.Message}); err != nil { log.Critical(err) return err } log.Trace("pinged") default: log.Criticalf("incorrect frame type: %v", frame.Type) return ERROR_INCORRECT_FRAME_TYPE } case frame := <-ch_ipc: // forward async messages from interprocess(goroutines) communication if err := stream.Send(frame); err != nil { log.Critical(err) return err } } } }
// client protocol handle proxy func proxy_user_request(sess *Session, p []byte) []byte { start := time.Now() defer utils.PrintPanicStack(sess, p) // 解密 if sess.Flag&SESS_ENCRYPT != 0 { sess.Decoder.XORKeyStream(p, p) } // 封装为reader reader := packet.Reader(p) // 读客户端数据包序列号(1,2,3...) // 可避免重放攻击-REPLAY-ATTACK seq_id, err := reader.ReadU32() if err != nil { log.Error("read client timestamp failed:", err) sess.Flag |= SESS_KICKED_OUT return nil } // 数据包序列号验证 if seq_id != sess.PacketCount { log.Errorf("illegal packet sequence id:%v should be:%v size:%v", seq_id, sess.PacketCount, len(p)-6) sess.Flag |= SESS_KICKED_OUT return nil } // 读协议号 b, err := reader.ReadS16() if err != nil { log.Error("read protocol number failed.") sess.Flag |= SESS_KICKED_OUT return nil } // 根据协议号断做服务划分 var ret []byte if b > MAX_PROTO_NUM { if err := forward(sess, p[4:]); err != nil { log.Errorf("service id:%v execute failed, error:%v", b, err) sess.Flag |= SESS_KICKED_OUT return nil } } else { if h := client_handler.Handlers[b]; h != nil { ret = h(sess, reader) } else { log.Errorf("service id:%v not bind", b) sess.Flag |= SESS_KICKED_OUT return nil } } // 统计处理时间 elasped := time.Now().Sub(start) if b != 0 { // 排除心跳包日志 log.Trace("[REQ]", client_handler.RCode[b]) _statter.Timing(1.0, fmt.Sprintf("%v%v", STATSD_PREFIX, client_handler.RCode[b]), elasped) } return ret }
// PIPELINE #2 stream processing // the center of game logic func (s *server) Stream(stream GameService_StreamServer) error { defer PrintPanicStack() // session init var sess Session sess_die := make(chan struct{}) ch_agent := s.recv(stream, sess_die) ch_ipc := make(chan *Game_Frame, DEFAULT_CH_IPC_SIZE) defer func() { registry.Unregister(sess.UserId) close(sess_die) log.Trace("stream end:", sess.UserId) }() // read metadata from context md, ok := metadata.FromContext(stream.Context()) if !ok { log.Critical("cannot read metadata from context") return ERROR_INCORRECT_FRAME_TYPE } // read key if len(md["userid"]) == 0 { log.Critical("cannot read key:userid from metadata") return ERROR_INCORRECT_FRAME_TYPE } // parse userid userid, err := strconv.Atoi(md["userid"][0]) if err != nil { log.Critical(err) return ERROR_INCORRECT_FRAME_TYPE } // register user sess.UserId = int32(userid) registry.Register(sess.UserId, ch_ipc) log.Finef("userid %v logged in", sess.UserId) // >> main message loop << for { select { case frame, ok := <-ch_agent: // frames from agent if !ok { // EOF return nil } switch frame.Type { case Game_Message: // the passthrough message from client->agent->game // locate handler by proto number reader := packet.Reader(frame.Message) c, err := reader.ReadS16() if err != nil { log.Critical(err) return err } handle := client_handler.Handlers[c] if handle == nil { log.Criticalf("service not bind: %v", c) return ERROR_SERVICE_NOT_BIND } // handle request ret := handle(&sess, reader) // construct frame & return message from logic if ret != nil { if err := stream.Send(&Game_Frame{Type: Game_Message, Message: ret}); err != nil { log.Critical(err) return err } } // session control by logic if sess.Flag&SESS_KICKED_OUT != 0 { // logic kick out if err := stream.Send(&Game_Frame{Type: Game_Kick}); err != nil { log.Critical(err) return err } return nil } case Game_Ping: if err := stream.Send(&Game_Frame{Type: Game_Ping, Message: frame.Message}); err != nil { log.Critical(err) return err } log.Trace("pinged") default: log.Criticalf("incorrect frame type: %v", frame.Type) return ERROR_INCORRECT_FRAME_TYPE } case frame := <-ch_ipc: // forward async messages from interprocess(goroutines) communication if err := stream.Send(frame); err != nil { log.Critical(err) return err } } } }