Esempio n. 1
0
func (ns *numbers) init(path string) {
	ns.tables = make(map[string]*table)
	client := etcdclient.GetClient()
	defer func() {
		client.Close()
	}()

	resp, err := client.Get(path, false, false)
	if err != nil {
		log.Critical(err)
		return
	}

	// 解码xlsx
	xlsx_bin, err := base64.StdEncoding.DecodeString(resp.Node.Value)
	if err != nil {
		log.Critical(err)
		return
	}

	// 读取xlsx
	xlsx_reader, err := xlsx.OpenBinary(xlsx_bin)
	if err != nil {
		log.Critical(err)
		return
	}
	ns.parse(xlsx_reader.Sheets)
}
Esempio n. 2
0
func (s *server) init_machine_id() {
	client := <-s.client_pool
	defer func() { s.client_pool <- client }()

	for {
		// get the key
		resp, err := client.Get(context.Background(), UUID_KEY, nil)
		if err != nil {
			log.Critical(err)
			os.Exit(-1)
		}

		// get prevValue & prevIndex
		prevValue, err := strconv.Atoi(resp.Node.Value)
		if err != nil {
			log.Critical(err)
			os.Exit(-1)
		}
		prevIndex := resp.Node.ModifiedIndex

		// CompareAndSwap
		resp, err = client.Set(context.Background(), UUID_KEY, fmt.Sprint(prevValue+1), &etcd.SetOptions{PrevIndex: prevIndex})
		if err != nil {
			cas_delay()
			continue
		}

		// record serial number of this service, already shifted
		s.machine_id = (uint64(prevValue+1) & MACHINE_ID_MASK) << 12
		return
	}
}
Esempio n. 3
0
// 密钥交换
func P_get_seed_req(sess *Session, reader *packet.Packet) []byte {
	tbl, _ := PKT_seed_info(reader)
	// KEY1
	X1, E1 := dh.DHExchange()
	KEY1 := dh.DHKey(X1, big.NewInt(int64(tbl.F_client_send_seed)))

	// KEY2
	X2, E2 := dh.DHExchange()
	KEY2 := dh.DHKey(X2, big.NewInt(int64(tbl.F_client_receive_seed)))

	ret := seed_info{int32(E1.Int64()), int32(E2.Int64())}
	// 服务器加密种子是客户端解密种子
	encoder, err := rc4.NewCipher([]byte(fmt.Sprintf("%v%v", SALT, KEY2)))
	if err != nil {
		log.Critical(err)
		return nil
	}
	decoder, err := rc4.NewCipher([]byte(fmt.Sprintf("%v%v", SALT, KEY1)))
	if err != nil {
		log.Critical(err)
		return nil
	}
	sess.Encoder = encoder
	sess.Decoder = decoder
	sess.Flag |= SESS_KEYEXCG
	return packet.Pack(Code["get_seed_ack"], ret, nil)
}
Esempio n. 4
0
//---------------------------------------------------------- 产生GridFS文件
func SaveFile(filename string, buf []byte) bool {
	ms := _global_ms.Copy()
	defer ms.Close()

	gridfs := ms.DB("").GridFS("fs")

	// 首先删除同名文件
	err := gridfs.Remove(filename)
	if err != nil {
		log.Critical("gridfs", filename, err)
		return false
	}

	// 产生新文件
	file, err := gridfs.Create(filename)
	if err != nil {
		log.Critical("gridfs", filename, err)
		return false
	}

	n, err := file.Write(buf)
	if err != nil {
		log.Critical("gridfs", filename, n, err)
		return false
	}

	err = file.Close()
	if err != nil {
		log.Critical("gridfs", filename, err)
		return false
	}
	log.Info("gridfs", filename, "saved to GridFS!!")
	return true
}
Esempio n. 5
0
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()
}
Esempio n. 6
0
// get next value of a key, like auto-increment in mysql
func (s *server) Next(ctx context.Context, in *pb.Snowflake_Key) (*pb.Snowflake_Value, error) {
	client := <-s.client_pool
	defer func() { s.client_pool <- client }()
	key := PATH + in.Name
	for {
		// get the key
		resp, err := client.Get(context.Background(), key, nil)
		if err != nil {
			log.Critical(err)
			return nil, errors.New("Key not exists, need to create first")
		}

		// get prevValue & prevIndex
		prevValue, err := strconv.Atoi(resp.Node.Value)
		if err != nil {
			log.Critical(err)
			return nil, errors.New("marlformed value")
		}
		prevIndex := resp.Node.ModifiedIndex

		// CompareAndSwap
		resp, err = client.Set(context.Background(), key, fmt.Sprint(prevValue+1), &etcd.SetOptions{PrevIndex: prevIndex})
		if err != nil {
			cas_delay()
			continue
		}
		return &pb.Snowflake_Value{int64(prevValue + 1)}, nil
	}
}
Esempio n. 7
0
// get next value of a key, like auto-increment in mysql
func (s *server) Next(ctx context.Context, in *pb.Snowflake_Key) (*pb.Snowflake_Value, error) {
	client := s.client_pool.Get().(*etcd.Client)
	defer func() {
		s.client_pool.Put(client)
	}()
	key := PATH + in.Name

	for i := 0; i < RETRY_MAX; i++ {
		// get the key
		resp, err := client.Get(key, false, false)
		if err != nil {
			log.Critical(err)
			return nil, errors.New("Key not exists, need to create first")
		}

		// get prevValue & prevIndex
		prevValue, err := strconv.Atoi(resp.Node.Value)
		if err != nil {
			log.Critical(err)
			return nil, errors.New("marlformed value")
		}
		prevIndex := resp.Node.ModifiedIndex

		// CAS
		resp, err = client.CompareAndSwap(key, fmt.Sprint(prevValue+1), 0, resp.Node.Value, prevIndex)
		if err != nil {
			log.Error(err)
			<-time.After(RETRY_DELAY)
			continue
		}
		return &pb.Snowflake_Value{int64(prevValue + 1)}, nil
	}
	return nil, errors.New("etcd server busy")
}
Esempio n. 8
0
func (s *server) restore() {
	// restore data from db file
	db := s.open_db()
	defer db.Close()
	count := 0
	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(BOLTDB_BUCKET))
		b.ForEach(func(k, v []byte) error {
			var msg []Chat_Message
			err := msgpack.Unmarshal(v, &msg)
			if err != nil {
				log.Critical("chat data corrupted:", err)
				os.Exit(-1)
			}
			id, err := strconv.ParseUint(string(k), 0, 64)
			if err != nil {
				log.Critical("chat data corrupted:", err)
				os.Exit(-1)
			}
			ep := NewEndPoint()
			ep.inbox = msg
			s.eps[id] = ep
			count++
			return nil
		})
		return nil
	})

	log.Infof("restored %v chats", count)
}
Esempio n. 9
0
func (s *server) dump(db *bolt.DB, changes map[uint64]bool) {
	db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(BOLTDB_BUCKET))
		for k := range changes {
			ep := s.read_ep(k)
			if ep == nil {
				log.Errorf("cannot find endpoint %v", k)
				continue
			}

			// serialization and save
			bin, err := msgpack.Marshal(ep.Read())
			if err != nil {
				log.Critical("cannot marshal:", err)
				continue
			}
			err = b.Put([]byte(fmt.Sprint(k)), bin)
			if err != nil {
				log.Critical(err)
				continue
			}
		}
		return nil
	})
}
Esempio n. 10
0
// 玩家登陆过程
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)
}
Esempio n. 11
0
func (s *db) Set(tbl string, uid int32, data interface{}) error {
	bin, err := msgpack.Marshal(data)
	if err != nil {
		log.Critical(err)
		return err
	}
	_, err = s.redis_client.Cmd("SET", Key(tbl, uid), bin).Str()
	if err != nil {
		log.Critical(err)
		return err
	}
	return nil
}
Esempio n. 12
0
func (s *db) Get(tbl string, uid int32, data interface{}) error {
	raw, err := s.redis_client.Cmd("GET", Key(tbl, uid)).Bytes()
	if err != nil {
		log.Critical(err)
		return err
	}
	// unpack message from msgpack format
	err = msgpack.Unmarshal(raw, &data)
	if err != nil {
		log.Critical(err)
		return err
	}
	return nil
}
Esempio n. 13
0
// 玩家登陆过程
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)
}
Esempio n. 14
0
// stream receiver
func (s *server) recv(stream GameService_StreamServer) chan *Game_Frame {
	ch := make(chan *Game_Frame, 1)
	go func() {
		for {
			in, err := stream.Recv()
			if err == io.EOF { // client closed
				close(ch)
				return
			}

			if err != nil {
				log.Critical(err)
				close(ch)
				return
			}
			select {
			case ch <- in:
			case <-time.After(RECV_TIMEOUT):
				log.Warning("recv deliver timeout")
				close(ch)
				return
			}
		}
	}()
	return ch
}
Esempio n. 15
0
// stream receiver
func (s *server) recv(stream GameService_StreamServer, sess_die chan struct{}) chan *Game_Frame {
	ch := make(chan *Game_Frame, 1)
	go func() {
		defer func() {
			close(ch)
		}()
		for {
			in, err := stream.Recv()
			if err == io.EOF { // client closed
				return
			}

			if err != nil {
				log.Critical(err)
				return
			}
			select {
			case ch <- in:
			case <-sess_die:
				return
			}
		}
	}()
	return ch
}
Esempio n. 16
0
func (s *server) init() {
	// get an unique value for consumer channel of nsq
	s.machines = []string{DEFAULT_ETCD}
	if env := os.Getenv("ETCD_HOST"); env != "" {
		s.machines = strings.Split(env, ";")
	}

	s.client_pool = make(chan *etcd.Client, CLIENT_MAX)

	// init client pool
	for i := 0; i < CLIENT_MAX; i++ {
		s.client_pool <- etcd.NewClient(s.machines)
	}

	// check if user specified machine id is set
	if env := os.Getenv(ENV_MACHINE_ID); env != "" {
		if id, err := strconv.Atoi(env); err == nil {
			s.machine_id = (uint64(id) & MACHINE_ID_MASK) << 12
			log.Info("machine id specified:", id)
		} else {
			log.Critical(err)
			os.Exit(-1)
		}
	} else {
		s.init_machine_id()
	}
}
Esempio n. 17
0
func (s *server) dump(db *bolt.DB, changes map[uint64]bool) {
	for k := range changes {
		// marshal
		var rs *RankSet
		s.lock_read(func() {
			rs = s.ranks[k]
		})
		if rs == nil {
			log.Warning("empty rankset:", k)
			continue
		}

		// serialization and save
		bin, err := rs.Marshal()
		if err != nil {
			log.Critical("cannot marshal:", err)
			os.Exit(-1)
		}

		db.Update(func(tx *bolt.Tx) error {
			b := tx.Bucket([]byte(BOLTDB_BUCKET))
			err := b.Put([]byte(fmt.Sprint(k)), bin)
			return err
		})
	}
}
Esempio n. 18
0
// publish to nsqd (localhost nsqd is suggested!)
func Publish(r *RedoRecord) {
	// pack message
	pack, err := msgpack.Marshal(r)
	if err != nil {
		log.Critical(err)
		return
	}

	// post to nsqd
	resp, err := http.Post(_pub_addr, MIME, bytes.NewReader(pack))
	if err != nil {
		log.Critical(err)
		return
	}
	defer resp.Body.Close()
}
Esempio n. 19
0
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")
}
Esempio n. 20
0
func Key(tbl string, uid int32) string {
	if tbl == "" || uid == 0 {
		log.Critical("Key error: %+v: %+v", tbl, uid)
		return ""
	}
	return fmt.Sprintf("%s:%s", tbl, uid)
}
Esempio n. 21
0
// watcher for data change in etcd directory
func (p *service_pool) watcher() {
	client := p.client_pool.Get().(*etcd.Client)
	defer func() {
		p.client_pool.Put(client)
	}()

	for {
		ch := make(chan *etcd.Response, 10)
		go func() {
			for {
				if resp, ok := <-ch; ok {
					if resp.Node.Dir {
						continue
					}
					key, value := resp.Node.Key, resp.Node.Value
					if value == "" {
						log.Tracef("node delete: %v", key)
						p.remove_service(key)
					} else {
						log.Tracef("node add: %v %v", key, value)
						p.add_service(key, value)
					}
				} else {
					return
				}
			}
		}()

		_, err := client.Watch(DEFAULT_SERVICE_PATH, 0, true, ch, nil)
		if err != nil {
			log.Critical(err)
		}
		<-time.After(RETRY_DELAY)
	}
}
Esempio n. 22
0
func init() {
	mongo_host := DEFAULT_MONGODB_URL
	if env := os.Getenv(ENV_MONGODB_URL); env != "" {
		mongo_host = env
	}
	// dial mongodb
	sess, err := mgo.Dial(mongo_host)
	if err != nil {
		log.Critical("mongodb: cannot connect to", mongo_host, err)
		os.Exit(-1)
	}

	// set default session mode to strong for saving player's data
	sess.SetMode(mgo.Strong, true)
	// set a high timout
	sess.SetSocketTimeout(DEFAULT_MGO_TIMEOUT * time.Second)
	// infinite wait cursor
	sess.SetCursorTimeout(0)
	_global_ms = sess

	// value
	valve := DEFAULT_DBOPS_VALVE
	_valve_dbops = make(chan bool, valve)
	_valve_dbops_high = make(chan bool, valve)

	// high priority caller
	_high_prio = make(map[string]bool)
	_high_prio[`agent/AI.LoginProc`] = true
	_high_prio[`agent/net.P_user_login_req`] = true
	_high_prio[`main._flush`] = true
}
Esempio n. 23
0
// 玩家登陆过程
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)
}
Esempio n. 24
0
File: arch.go Progetto: en/archiver
func (arch *Archiver) archive_task() {
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGTERM)
	timer := time.After(REDO_ROTATE_INTERVAL)
	sync_ticker := time.NewTicker(SYNC_INTERVAL)
	db := arch.new_redolog()
	key := make([]byte, 8)
	for {
		select {
		case <-sync_ticker.C:
			n := len(arch.pending)
			if n == 0 {
				continue
			}

			db.Update(func(tx *bolt.Tx) error {
				b := tx.Bucket([]byte(BOLTDB_BUCKET))
				for i := 0; i < n; i++ {
					id, err := b.NextSequence()
					if err != nil {
						log.Critical(err)
						continue
					}
					binary.BigEndian.PutUint64(key, uint64(id))
					if err = b.Put(key, <-arch.pending); err != nil {
						log.Critical(err)
						continue
					}
				}
				return nil
			})
		case <-timer:
			db.Close()
			// rotate redolog
			db = arch.new_redolog()
			timer = time.After(REDO_ROTATE_INTERVAL)
		case <-sig:
			db.Close()
			log.Info("SIGTERM")
			os.Exit(0)
		}
	}
}
Esempio n. 25
0
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.")
}
Esempio n. 26
0
func init() {
	addr := DEFAULT_STATSD_HOST
	if env := os.Getenv(ENV_STATSD); env != "" {
		addr = env
	}

	s, err := g2s.Dial("udp", addr)
	if err != nil {
		log.Critical(err)
		os.Exit(-1)
	}
	_statter = s
}
Esempio n. 27
0
func (s *server) init() {
	// snappy
	if env := os.Getenv(ENV_SNAPPY); env != "" {
		s.enable_snappy = true
	}

	// read redis host
	redis_host := DEFAULT_REDIS_HOST
	if env := os.Getenv(ENV_REDIS_HOST); env != "" {
		redis_host = env
	}
	// start connection to redis cluster
	client, err := cluster.NewCluster(redis_host)
	if err != nil {
		log.Critical(err)
		os.Exit(-1)
	}
	s.redis_client = client

	// read mongodb host
	mongodb_url := DEFAULT_MONGODB_URL
	if env := os.Getenv(ENV_MONGODB_URL); env != "" {
		mongodb_url = env
	}

	// start connection to mongodb
	sess, err := mgo.Dial(mongodb_url)
	if err != nil {
		log.Critical(err)
		os.Exit(-1)
	}
	// database is provided in url
	s.db = sess.DB("")

	// wait chan
	s.wait = make(chan string, BUFSIZ)
	go s.loader_task()
}
Esempio n. 28
0
func (s *server) init_machine_id() {
	client := s.client_pool.Get().(*etcd.Client)
	defer func() {
		s.client_pool.Put(client)
	}()

	for i := 0; i < RETRY_MAX; i++ {
		// get the key
		resp, err := client.Get(UUID_KEY, false, false)
		if err != nil {
			log.Critical(err)
			os.Exit(-1)
		}

		// get prevValue & prevIndex
		prevValue, err := strconv.Atoi(resp.Node.Value)
		if err != nil {
			log.Critical(err)
			os.Exit(-1)
		}
		prevIndex := resp.Node.ModifiedIndex

		// CAS
		resp, err = client.CompareAndSwap(UUID_KEY, fmt.Sprint(prevValue+1), 0, resp.Node.Value, prevIndex)
		if err != nil {
			log.Error(err)
			<-time.After(RETRY_DELAY)
			continue
		}

		// record serial number of this service, already shifted
		s.machine_id = (uint64(prevValue+1) & MACHINE_ID_MASK) << 12
		return
	}

	// failed to get machine id, exit
	os.Exit(-1)
}
Esempio n. 29
0
func (ns *numbers) init(path string) {
	ns.tables = make(map[string]*table)
	kapi := etcdclient.KeysAPI()
	resp, err := kapi.Get(context.Background(), path, nil)
	if err != nil {
		log.Error(err)
		return
	}

	// 解码xlsx
	xlsx_bin, err := base64.StdEncoding.DecodeString(resp.Node.Value)
	if err != nil {
		log.Critical(err)
		return
	}

	// 读取xlsx
	xlsx_reader, err := xlsx.OpenBinary(xlsx_bin)
	if err != nil {
		log.Critical(err)
		return
	}
	ns.parse(xlsx_reader.Sheets)
}
Esempio n. 30
0
func (s *server) restore() {
	// restore data from db file
	db := s.open_db()
	defer db.Close()
	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(BOLTDB_BUCKET))
		b.ForEach(func(k, v []byte) error {
			rs := NewRankSet()
			err := rs.Unmarshal(v)
			if err != nil {
				log.Critical("rank data corrupted:", err)
				os.Exit(-1)
			}
			id, err := strconv.ParseUint(string(k), 0, 64)
			if err != nil {
				log.Critical("chat data corrupted:", err)
				os.Exit(-1)
			}
			s.ranks[id] = rs
			return nil
		})
		return nil
	})
}