Browse Source

developing

joe 4 years ago
parent
commit
4b0af85d23

+ 73 - 7
agent/main.go

@@ -3,9 +3,17 @@ package main
 import (
 	"encoding/json"
 	"git.wanbits.io/joe/franklin/comp"
-	"git.wanbits.io/joe/franklin/protos"
+	pb "git.wanbits.io/joe/franklin/protos"
+	"git.wanbits.io/joe/kettle/htp"
+	"git.wanbits.io/joe/kettle/log"
 	"git.wanbits.io/joe/kettle/utl"
 	"git.wanbits.io/joe/nnet/cpn"
+	"github.com/coreos/etcd/clientv3"
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"net/http"
+	"syscall"
+	"time"
 )
 
 func main() {
@@ -21,23 +29,81 @@ func main() {
 	comp.InitAll(g_conf)
 
 	// connect to login
-	loginjs, err := comp.GEtcdc.Get(comp.PathLogin(g_conf))
+	loginconf, err := comp.GEtcdc.Get(comp.PathLogin(g_conf))
 	utl.ErrPanic(err)
+	if len(loginconf.Kvs) <= 0 {
+		utl.ErrPanic(utl.ErrContainerEmpty)
+	}
 
-	var loginConf protos.LoginConfConf
-	err = json.Unmarshal(loginjs, &loginConf)
+	var loginConf pb.LoginConfConf
+	err = json.Unmarshal(loginconf.Kvs[0].Value, &loginConf)
 	utl.ErrPanic(err)
 
-	rabbitProto := RabbitCProtocol{}
-	g_rabbit = cpn.NewWsClient(&hubConfC, &rabbitProto, &rabbitProto)
-	//TODO: add feature auto-reconnect
+	g_rabbitProto = NewRabbitCProtocol()
+	g_rabbit = cpn.NewWsClient(&hubConfC, g_rabbitProto, g_rabbitProto, cpn.WithReconn(3*time.Second))
 	err = g_rabbit.NewConnection(loginConf.Addrs[0], g_conf.Id)
 	utl.UnUsed(err)
 
 	// connect to lobbies
+	lobJs, err := comp.GEtcdc.Get(comp.PathLobbies(g_conf), clientv3.WithPrefix())
+	utl.ErrPanic(err)
+
+	lobProto := ALProtocol{}
+	g_taj = cpn.NewTcpClient(&hubConfC, &lobProto, &lobProto, cpn.WithReconn(3*time.Second))
+
+	var lobConf pb.AppConfConf
+	for _, conf := range lobJs.Kvs {
+		err = json.Unmarshal(conf.Value, &lobConf)
+		utl.ErrPanic(err)
+
+		err = g_taj.NewConnection(lobConf.Addr, lobConf.Id)
+		utl.UnUsed(err)
+	}
+
 	// connect to logics
+	lgcJs, err := comp.GEtcdc.Get(comp.PathLogics(g_conf), clientv3.WithPrefix())
+	utl.ErrPanic(err)
+
+	lgcProto := ALProtocol{}
+	g_logics = cpn.NewTcpClient(&hubConfC, &lgcProto, &lgcProto, cpn.WithReconn(3*time.Second))
+
+	var lgcConf pb.AppConfConf
+	for _, conf := range lgcJs.Kvs {
+		err = json.Unmarshal(conf.Value, &lgcConf)
+		utl.ErrPanic(err)
+
+		err = g_logics.NewConnection(lgcConf.Addr, lobConf.Id)
+		utl.UnUsed(err)
+	}
 	// start rpc
+	comp.GRpc = htp.NewGinServer(g_conf.RpcAddr, htp.WithCors(true))
+	err = comp.GRpc.Start(func(e *gin.Engine) {
+		e.POST("/rpc", do_dispatch)
+	})
+	utl.ErrPanic(err)
+
 	// register self
+	// watch etcd
 	// start listener
+	g_serverProto = &CSProtocol{}
+	g_server = cpn.NewWsServer(&hubConfS, g_serverProto, g_serverProto, g_conf.Addr, "/franklin", nil)
+	go func() {
+		if err = g_server.Start(); err != nil && err != http.ErrServerClosed {
+			utl.ErrPanic(err)
+		}
+	}()
 	// wait signal
+	sig := utl.WaitForSignals(syscall.SIGINT)
+	log.Debug("received signal", zap.String("sig", sig.String()))
+
+	_ = g_server.Stop()
+	_ = comp.GRpc.Stop()
+	_ = g_logics.Stop()
+	_ = g_taj.Stop()
+	_ = g_rabbit.Stop()
+	comp.GMq.Close()
+	comp.GEtcdc.Close()
+	_ = comp.GRds.Close()
+	comp.GEv.Close()
+	log.Sync()
 }

+ 133 - 17
agent/rabbit.go

@@ -3,32 +3,127 @@ package main
 import (
 	"encoding/json"
 	"git.wanbits.io/joe/franklin/comp"
+	pb "git.wanbits.io/joe/franklin/protos"
 	"git.wanbits.io/joe/kettle/log"
+	"git.wanbits.io/joe/kettle/mac"
 	"git.wanbits.io/joe/kettle/utl"
 	"git.wanbits.io/joe/nnet"
 	"go.uber.org/zap"
+	"time"
 )
 
-type WsRespPacket struct {
-	Cmd    string
-	Ec     int
-	Result map[string]interface{}
+const (
+	LOGIC_SESSION_ID = 5
+	LOGIN_TIMEOUT    = 2 * time.Second
+
+	CMD_HEARTBEAT       = "bl_heartbeat"
+	CMD_LOGIN_VERIFY    = "bl_login_verify"
+	CMD_UPDATE_USERINFO = "bl_update_userinfo"
+)
+
+type WaitParam struct {
+	ses   nnet.ISession
+	timer *time.Timer
+}
+
+// TODO: optimize with Pool
+func NewWaitParam(ses nnet.ISession, timeout time.Duration) *WaitParam {
+	return &WaitParam{
+		ses:   ses,
+		timer: time.NewTimer(timeout),
+	}
+}
+
+//
+//type WaitorParam struct {
+//	ses nnet.ISession
+//	ch  chan struct{}
+//}
+//
+//type PacketWaitor struct {
+//	mp *utl.SMap
+//}
+//
+//func NewPacketWaitor() *PacketWaitor {
+//	return &PacketWaitor{
+//		mp: utl.NewSMap(),
+//	}
+//}
+//
+//func Wait(key interface{}, ses nnet.ISession) {
+//
+//}
+
+func (self *RabbitCProtocol) Send(packet *ReqPacket, cses nnet.ISession) error {
+	ses, err := g_rabbit.GetSession(LOGIC_SESSION_ID)
+	if err != nil {
+		log.Warn("RabbitSend", zap.Error(err))
+		return err
+	}
+
+	chWait := NewWaitParam(cses, LOGIN_TIMEOUT)
+	err = g_rabbitProto.Insert(packet.Seq, chWait)
+	if err != nil {
+		return err
+	}
+
+	go func() {
+		select {
+		case <-chWait.timer.C:
+			g_rabbitProto.Del(packet.Seq)
+			on_recv_timeout(cses, packet)
+		}
+	}()
+
+	return ses.AWrite(packet, time.Second*2)
 }
 
-func (self *WsRespPacket) ShouldClose() (bool, int32) {
+type RespPacket struct {
+	*utl.JsResp
+}
+
+func NewRespPacket(cmd string, seq int64, ec int, res interface{}) *RespPacket {
+	return &RespPacket{
+		&utl.JsResp{
+			Cmd:    cmd,
+			Seq:    seq,
+			Ec:     ec,
+			Result: res,
+		},
+	}
+}
+
+func (self *RespPacket) ShouldClose() (bool, int32) {
 	return false, 0
 }
 
-type WsReqPacket struct {
-	Cmd    string
-	Params map[string]interface{}
+type ReqPacket struct {
+	*utl.JsReq
+}
+
+func NewReqPacket(cmd string, seq int64, params interface{}) *ReqPacket {
+	return &ReqPacket{
+		&utl.JsReq{
+			Cmd:    cmd,
+			Seq:    seq,
+			Params: params,
+		},
+	}
 }
 
-func (self *WsReqPacket) ShouldClose() (bool, int32) {
+func (self *ReqPacket) ShouldClose() (bool, int32) {
 	return false, 0
 }
 
-type RabbitCProtocol struct{}
+type RabbitCProtocol struct {
+	*utl.SMap
+}
+
+func NewRabbitCProtocol() *RabbitCProtocol {
+	return &RabbitCProtocol{
+		utl.NewSMap(),
+	}
+}
 
 func (self *RabbitCProtocol) ReadPacket(conn nnet.IConn) (nnet.IPacket, error) {
 	buf := comp.GBufPool.Get()
@@ -39,7 +134,7 @@ func (self *RabbitCProtocol) ReadPacket(conn nnet.IConn) (nnet.IPacket, error) {
 		return nil, err
 	}
 
-	p := &WsRespPacket{}
+	p := &RespPacket{}
 	err = json.Unmarshal(buf[:n], p)
 	if err != nil {
 		return nil, err
@@ -49,7 +144,7 @@ func (self *RabbitCProtocol) ReadPacket(conn nnet.IConn) (nnet.IPacket, error) {
 }
 
 func (self *RabbitCProtocol) Serialize(packet nnet.IPacket) ([]byte, error) {
-	p, ok := packet.(*WsReqPacket)
+	p, ok := packet.(*ReqPacket)
 	if !ok {
 		return nil, utl.ErrInterfaceTransform
 	}
@@ -63,32 +158,53 @@ func (self *RabbitCProtocol) Serialize(packet nnet.IPacket) ([]byte, error) {
 }
 
 func (self *RabbitCProtocol) OnClosed(ses nnet.ISession, reason int32) {
+	log.Debug("closed connection to login", zap.Int32("reason", reason))
+	g_rabbit.StartReconn(ses.ServerAddr(), ses.Id())
 }
 
 func (self *RabbitCProtocol) OnConnected(ses nnet.ISession) (bool, int32) {
+	log.Debug("connected to login server", zap.String("addr", ses.ServerAddr()))
+	ses.UpdateId(LOGIC_SESSION_ID)
 	return true, 0
 }
 
 func (self *RabbitCProtocol) OnMessage(ses nnet.ISession, pkt nnet.IPacket) bool {
-	p, ok := pkt.(*WsRespPacket)
+	resp, ok := pkt.(*RespPacket)
 	if !ok {
-		log.Error("not a WsPacket")
+		log.Error("not a RespPacket")
 		return false
 	}
 
 	// dispatch
-	cb, ok := rabbit_route_table[p.Cmd]
+	cb, ok := rabbit_route_table[resp.Cmd]
 	if !ok {
-		log.Error("not supported cmd", zap.String("cmd", p.Cmd))
+		log.Error("unsupported cmd", zap.String("cmd", resp.Cmd))
 		return false
 	}
 
-	cb(ses, p)
+	cb(ses, resp)
 
 	return true
 }
 
 // server-side dont handle heartbeat.
 func (self *RabbitCProtocol) OnHeartbeat(ses nnet.ISession) bool {
+	mac.GetMachInfoMutablePart(&comp.Gmi)
+	var cpuPercent int32 = 0
+	if len(comp.Gmi.CpuPercent) > 0 {
+		cpuPercent = int32(comp.Gmi.CpuPercent[0])
+	}
+	seq := time.Now().UnixNano()
+	p := NewReqPacket("bl_heartbeat", seq, &pb.StatusParams{
+		Id:      g_conf.Id,
+		Ts:      time.Now().Unix(),
+		Active:  int32(g_server.GetSessionNum()),
+		Support: 3000,
+		Mem:     int32(comp.Gmi.MemPercent),
+		Cpu:     cpuPercent,
+	})
+
+	_ = self.Send(p, nil)
+
 	return true
 }

+ 106 - 4
agent/rabbit_router.go

@@ -1,11 +1,113 @@
 package main
 
-import "git.wanbits.io/joe/nnet"
+import (
+	"git.wanbits.io/joe/franklin/mod"
+	pb "git.wanbits.io/joe/franklin/protos"
+	"git.wanbits.io/joe/kettle/log"
+	"git.wanbits.io/joe/nnet"
+	"github.com/go-redis/redis"
+	"github.com/mitchellh/mapstructure"
+	"go.uber.org/zap"
+)
 
-type fnRabbitCallback func(session nnet.ISession, packet *WsRespPacket)
+type fnRabbitCallback func(session nnet.ISession, packet *RespPacket)
 
 var (
-	rabbit_route_table = map[string]fnRabbitCallback {
-
+	rabbit_route_table = map[string]fnRabbitCallback{
+		CMD_HEARTBEAT:       on_bl_heartbeat,
+		CMD_LOGIN_VERIFY:    on_bl_login_verify,
+		CMD_UPDATE_USERINFO: on_bl_update_userinfo,
 	}
 )
+
+func on_recv_timeout(cses nnet.ISession, pkt *ReqPacket) {
+	switch pkt.Cmd {
+	case CMD_LOGIN_VERIFY:
+		response_c_login(cses, pb.ErrCode_TIMEOUT, nil)
+	case CMD_UPDATE_USERINFO:
+		log.Error("udpate userinfo timeout", zap.Any("param", pkt))
+	}
+}
+
+func on_bl_heartbeat(ses nnet.ISession, pkt *RespPacket) {
+
+}
+
+func on_bl_login_verify(ses nnet.ISession, pkt *RespPacket) {
+	iwp, ok := g_rabbitProto.Get(pkt.Seq)
+	if !ok {
+		return // timeout
+	}
+	wp, ok := iwp.(*WaitParam)
+	if !ok {
+		log.Error("interface transform failed")
+		return
+	}
+	if !wp.timer.Stop() {
+		return // timeout
+	}
+
+	resp := &pb.VerifyResult{}
+	err := mapstructure.Decode(pkt.Result, resp)
+	if err != nil {
+		response_c_login(wp.ses, pb.ErrCode_UNKNOWN, nil)
+		log.Error("bad format", zap.Error(err))
+		return
+	}
+
+	var s_login *pb.S_Login
+	user, err := setget_user(resp)
+	if err != nil {
+		s_login = &pb.S_Login{
+			UserId:   resp.UserId,
+			Username: resp.Username,
+			Nickname: resp.Nickname,
+		}
+	} else {
+		s_login = &pb.S_Login{
+			UserId:   user.UserId,
+			Username: user.Username,
+			Nickname: user.Nickname,
+		}
+		if user.Status != int32(pb.UserStatus_NORMAL) {
+			response_c_login(wp.ses, pb.ErrCode_FORBIDDEN, nil)
+			return
+		}
+		// check kick-off
+	}
+
+	response_c_login(wp.ses, pb.ErrCode_OK, s_login)
+}
+
+func setget_user(res *pb.VerifyResult) (*pb.User, error) {
+	user, err := mod.LoadUser(res.UserId)
+	if err != nil {
+		if err == redis.Nil {
+			user = &pb.User{
+				UserId:     res.UserId,
+				Username:   res.Username,
+				Nickname:   res.Nickname,
+				HeaderIcon: res.HeaderIcon,
+				Publish:    res.Publish,
+				Platform:   res.Platform,
+			}
+			fields, err := mod.StructToMap(user)
+			if err != nil {
+				log.Error("", zap.Error(err))
+				return user, nil
+			}
+			err = mod.UpdateUserAttrs(user.UserId, fields)
+			if err != nil {
+				log.Error("redis save", zap.Error(err))
+			}
+			return user, nil
+		}
+		log.Error("redis read", zap.Error(err))
+		return nil, err
+	}
+	return user, nil
+}
+
+func on_bl_update_userinfo(ses nnet.ISession, pkt *RespPacket) {
+
+}

+ 7 - 0
agent/rpc_router.go

@@ -0,0 +1,7 @@
+package main
+
+import "github.com/gin-gonic/gin"
+
+func do_dispatch(ctx *gin.Context) {
+
+}

+ 60 - 9
agent/server.go

@@ -1,19 +1,19 @@
 package main
 
 import (
-	"encoding/json"
 	"git.wanbits.io/joe/franklin/comp"
-	"git.wanbits.io/joe/franklin/protos"
+	pb "git.wanbits.io/joe/franklin/protos"
 	"git.wanbits.io/joe/kettle/log"
 	"git.wanbits.io/joe/kettle/utl"
 	"git.wanbits.io/joe/nnet"
 	"github.com/golang/protobuf/proto"
 	"go.uber.org/zap"
+	"time"
 )
 
 // C/S packet
 type CSPacket struct {
-	*protos.Request
+	*pb.Request
 }
 
 func (self *CSPacket) ShouldClose() (bool, int32) {
@@ -21,17 +21,34 @@ func (self *CSPacket) ShouldClose() (bool, int32) {
 }
 
 type SCPacket struct {
-	resp    *protos.Response
-	closing bool
+	resp    *pb.Response
+	closing int32
 	reason  int32
 }
 
+func NewSCPacket(resp *pb.Response, params ...int32) *SCPacket {
+	p := &SCPacket{
+		resp: resp,
+	}
+	if len(params) > 0 {
+		p.closing = params[0]
+	}
+	if len(params) > 1 {
+		p.reason = params[1]
+	}
+	return p
+}
+
 func (self *SCPacket) ShouldClose() (bool, int32) {
-	return self.closing, self.reason
+	close_session := false
+	if self.closing > 0 {
+		close_session = true
+	}
+	return close_session, self.reason
 }
 
 // protocol && callback
-type CSProtocol struct{
+type CSProtocol struct {
 }
 
 func (self *CSProtocol) ReadPacket(conn nnet.IConn) (nnet.IPacket, error) {
@@ -42,7 +59,7 @@ func (self *CSProtocol) ReadPacket(conn nnet.IConn) (nnet.IPacket, error) {
 	if err != nil {
 		return nil, err
 	}
-	var req *protos.Request
+	var req *pb.Request
 	err = proto.Unmarshal(buf[:n], req)
 	if err != nil {
 		return nil, err
@@ -58,12 +75,13 @@ func (self *CSProtocol) Serialize(packet nnet.IPacket) ([]byte, error) {
 	if !ok {
 		return nil, utl.ErrInterfaceTransform
 	}
-	return json.Marshal(p.resp)
+	return proto.Marshal(p.resp)
 }
 
 func (self *CSProtocol) OnClosed(ses nnet.ISession, reason int32) {
 	log.Debug("lost connection", zap.String("remote", ses.GetRawConn().RemoteAddr().String()),
 		zap.Uint64("id", ses.Id()))
+	on_C_Logout(ses, nil)
 }
 
 func (self *CSProtocol) OnConnected(ses nnet.ISession) (bool, int32) {
@@ -84,3 +102,36 @@ func (self *CSProtocol) OnMessage(ses nnet.ISession, pkt nnet.IPacket) bool {
 func (self *CSProtocol) OnHeartbeat(ses nnet.ISession) bool {
 	return true
 }
+
+// @ses: session
+// @st: Response 内部结构的 struct
+// @params: [0] 发送后是否关闭 [1] 关闭原因
+func SendToClient(ses nnet.ISession, userId uint64, ec pb.ErrCode, st proto.Message, params ...int32) {
+	// 不做 guid 合法性检查,而直接发出消息
+	bst, err := proto.Marshal(st)
+	if nil != err {
+		log.Error("proto.Marshal", zap.Error(err))
+		return
+	}
+	name := comp.GetMsgName(st)
+	id := comp.GetMsgId(name)
+	res := &pb.Response{
+		MsgId:   id,
+		UserId:  userId,
+		MsgName: name,
+		Ec:      int32(ec),
+		Res:     bst,
+	}
+
+	err = ses.AWrite(NewSCPacket(res, params...), time.Second)
+	if nil != err {
+		log.Error("proto.AWrite", zap.Error(err))
+	}
+}
+
+// 关闭某个用户连接
+func KillUserSession(ses nnet.ISession, userId uint64, reason pb.NetCloseReason) {
+	cmd_offline := &pb.S_Offline{}
+	cmd_offline.Reason = int32(reason)
+	SendToClient(ses, userId, pb.ErrCode_OK, cmd_offline, 1, int32(reason))
+}

+ 54 - 4
agent/router.go → agent/server_router.go

@@ -1,9 +1,11 @@
 package main
 
 import (
-	"git.wanbits.io/joe/franklin/protos"
+	"git.wanbits.io/joe/franklin/mod"
+	pb "git.wanbits.io/joe/franklin/protos"
 	"git.wanbits.io/joe/nnet"
 	"github.com/golang/protobuf/proto"
+	"time"
 )
 
 var (
@@ -31,24 +33,72 @@ func dispatch(ses nnet.ISession, pkt *CSPacket) {
 }
 
 func on_C_Heartbeat(ses nnet.ISession, p *CSPacket) {
-
+	resp := &pb.S_Heartbeat{
+		Ts: time.Now().Unix(),
+	}
+	SendToClient(ses, ses.Id(), pb.ErrCode_OK, resp)
 }
 
 func on_C_Login(ses nnet.ISession, p *CSPacket) {
-	prm := &protos.C_Login{}
+	prm := &pb.C_Login{}
+	ses.SetId(p.UserId)
 	err := proto.Unmarshal(p.Req, prm)
 	if err != nil {
+		response_c_login(ses, pb.ErrCode_BAD_FORMAT, nil)
 		return
 	}
 	// verify by rabbit
-	// chech agent offline
+	seq := time.Now().UnixNano()
+	err = g_rabbitProto.Send(NewReqPacket("bl_login_verify", seq, &pb.VerifyParams{
+		UserId:   p.UserId,
+		Username: prm.Username,
+		Token:    prm.Token,
+	}), ses)
+	if err != nil {
+		response_c_login(ses, pb.ErrCode_FAILED, nil)
+	}
+}
+
+func response_c_login(ses nnet.ISession, errCode pb.ErrCode, res *pb.S_Login) {
+	var ec = pb.ErrCode_OK
+	var reason = pb.NetCloseReason_KICK
+
+	defer func() {
+		p := []int32{}
+		if ec != pb.ErrCode_OK {
+			p = append(p, 1)
+			p = append(p, int32(reason))
+			res = &pb.S_Login{}
+			//SendToClient(ses, ses.Id(), ec, &pb.S_Login{})
+			//KillUserSession(ses, ses.Id(), pb.NetCloseReason_ERR_AUTH)
+		}
+		SendToClient(ses, ses.Id(), ec, res, p...)
+	}()
+
+	if errCode != pb.ErrCode_OK {
+		ec = errCode
+		reason = pb.NetCloseReason_ERR_AUTH
+		return
+	}
+	// check agent offline
+	if g_offline {
+		ec = pb.ErrCode_MAINTAINING
+		reason = pb.NetCloseReason_KICK
+		return
+	}
 	// check user status
 	// check user kicked off
+	if mod.LoadKick(ses.Id()) {
+		ec = pb.ErrCode_FORBIDDEN
+		reason = pb.NetCloseReason_KICK
+		return
+	}
 	// check if have logged in any agents already
 	// check reconnect state
 	// route to a lobby
 	// update user location
 	// update session id
+	ses.UpdateId(ses.Id())
 }
 
 func on_C_Logout(ses nnet.ISession, p *CSPacket) {

+ 9 - 5
agent/vars.go

@@ -1,7 +1,7 @@
 package main
 
 import (
-	"git.wanbits.io/joe/franklin/protos"
+	pb "git.wanbits.io/joe/franklin/protos"
 	"git.wanbits.io/joe/nnet"
 	"time"
 )
@@ -27,12 +27,16 @@ var (
 		ReadTimeout:    12 * time.Second, // should greater than Tick
 	}
 
-	g_conf *protos.AppConf
+	g_conf *pb.AppConf
 
-	g_server nnet.IHub
-	g_prot   *CSProtocol
+	g_server      nnet.IHub
+	g_serverProto *CSProtocol
+
+	g_rabbit      nnet.IHub
+	g_rabbitProto *RabbitCProtocol
 
-	g_rabbit nnet.IHub
 	g_logics nnet.IHub
 	g_taj    nnet.IHub // lobbies
+
+	g_offline bool
 )

+ 8 - 8
comp/config.go

@@ -1,14 +1,14 @@
 package comp
 
 import (
-	"git.wanbits.io/joe/franklin/protos"
+	pb "git.wanbits.io/joe/franklin/protos"
 	"git.wanbits.io/joe/kettle/utl"
 	"github.com/spf13/viper"
 	"path/filepath"
 )
 
-func LoadConf(fullfile string, defPaths ...string) (*protos.AppConf, error) {
-	c := protos.AppConf{}
+func LoadConf(fullfile string, defPaths ...string) (*pb.AppConf, error) {
+	c := pb.AppConf{}
 	dir, f := filepath.Split(fullfile)
 	if len(dir) <= 0 {
 		dir = "./"
@@ -39,21 +39,21 @@ func LoadConf(fullfile string, defPaths ...string) (*protos.AppConf, error) {
 	return &c, nil
 }
 
-func checkAppConf(c *protos.AppConf) error {
+func checkAppConf(c *pb.AppConf) error {
 	if c == nil {
-		return utl.ErrForCode(int(protos.ErrCode_INVALID_PARAMS))
+		return utl.ErrForCode(int(pb.ErrCode_INVALID_PARAMS))
 	}
 
 	if len(c.Namespace) <= 0 {
-		return utl.ErrForCode(int(protos.ErrCode_LENGTH))
+		return utl.ErrForCode(int(pb.ErrCode_LENGTH))
 	}
 
 	if _, err := Parse(c.Id); err != nil {
-		return utl.ErrForCode(int(protos.ErrCode_BAD_FORMAT))
+		return utl.ErrForCode(int(pb.ErrCode_BAD_FORMAT))
 	}
 
 	if len(c.Zoo.Addrs) <= 0 {
-		return utl.ErrForCode(int(protos.ErrCode_LENGTH))
+		return utl.ErrForCode(int(pb.ErrCode_LENGTH))
 	}
 
 	return nil

+ 10 - 10
comp/etcd_path.go

@@ -2,45 +2,45 @@ package comp
 
 import (
 	"fmt"
-	"git.wanbits.io/joe/franklin/protos"
+	pb "git.wanbits.io/joe/franklin/protos"
 )
 
 /// see docs/ConfManagement.md
 
 var (
-	PathRedis = func(c *protos.AppConf) string {
+	PathRedis = func(c *pb.AppConf) string {
 		return fmt.Sprintf("%s/conf/servers/redis", c.Namespace)
 	}
 
-	PathMq = func(c *protos.AppConf) string {
+	PathMq = func(c *pb.AppConf) string {
 		return fmt.Sprintf("%s/conf/servers/mq", c.Namespace)
 	}
 
-	PathEvBus = func(c *protos.AppConf) string {
+	PathEvBus = func(c *pb.AppConf) string {
 		return fmt.Sprintf("%s/conf/servers/evbus", c.Namespace)
 	}
 
-	PathLogin = func(c *protos.AppConf) string {
+	PathLogin = func(c *pb.AppConf) string {
 		return fmt.Sprintf("%s/conf/servers/login", c.Namespace)
 	}
 
-	PathKv = func(c *protos.AppConf) string {
+	PathKv = func(c *pb.AppConf) string {
 		return fmt.Sprintf("%s/conf/servers/kv", c.Namespace)
 	}
 
-	PathLobbies = func(c *protos.AppConf) string {
+	PathLobbies = func(c *pb.AppConf) string {
 		return fmt.Sprintf("%s/app/lobbies", c.Namespace)
 	}
 
-	PathAgents = func(c *protos.AppConf) string {
+	PathAgents = func(c *pb.AppConf) string {
 		return fmt.Sprintf("%s/app/agents", c.Namespace)
 	}
 
-	PathLogics = func(c *protos.AppConf) string {
+	PathLogics = func(c *pb.AppConf) string {
 		return fmt.Sprintf("%s/app/logics", c.Namespace)
 	}
 
-	PathManagers = func(c *protos.AppConf) string {
+	PathManagers = func(c *pb.AppConf) string {
 		return fmt.Sprintf("%s/app/managers", c.Namespace)
 	}
 )

+ 14 - 9
comp/etcdc.go

@@ -1,10 +1,10 @@
 package comp
 
 import (
+	"encoding/json"
 	"fmt"
-	"git.wanbits.io/joe/franklin/protos"
+	pb "git.wanbits.io/joe/franklin/protos"
 	"git.wanbits.io/joe/kettle/etcd"
-	"git.wanbits.io/joe/kettle/utl"
 	"time"
 )
 
@@ -12,16 +12,21 @@ var (
 	GEtcdc *etcd.EtcdClient
 )
 
-func ConnectEtcd(c *protos.ConfConf) (*etcd.EtcdClient, error) {
+func ConnectEtcd(c *pb.ConfConf) (*etcd.EtcdClient, error) {
 	GEtcdc, err := etcd.New(c.Addrs, c.Username, c.Password, time.Duration(c.Timeout)*time.Second)
 	return GEtcdc, err
 }
 
-func RegisterSelf(path string, c *protos.AppConf, val string) {
-	fullpath := fmt.Sprintf("%s/%v", path, c.Id)
-	leaseId, err := GEtcdc.PutWithLife(fullpath, val, 2)
-	utl.ErrPanic(err)
+func RegisterSelf(path string, c *pb.AppConf, conf *pb.AppConfConf) error {
+	sconf, err := json.Marshal(conf)
+	if err != nil {
+		return err
+	}
 
-	err = GEtcdc.KeepAlive(leaseId)
-	utl.ErrPanic(err)
+	fullpath := fmt.Sprintf("%s/%v", path, c.Id)
+	leaseId, err := GEtcdc.PutWithLife(fullpath, string(sconf), 2)
+	if err != nil {
+		return err
+	}
+	return GEtcdc.KeepAlive(leaseId)
 }

+ 2 - 2
comp/helper.go

@@ -1,7 +1,7 @@
 package comp
 
 import (
-	"git.wanbits.io/joe/franklin/protos"
+	pb "git.wanbits.io/joe/franklin/protos"
 	"github.com/golang/protobuf/proto"
 	"strings"
 )
@@ -17,7 +17,7 @@ func GetMsgName(st proto.Message) string {
 
 // 根据协议名称获得协议ID
 func GetMsgId(name string) int32 {
-	msg_id, ok := protos.MsgId_value[name]
+	msg_id, ok := pb.MsgId_value[name]
 	if !ok {
 		return -1
 	}

+ 17 - 9
comp/init.go

@@ -3,12 +3,12 @@ package comp
 import (
 	"encoding/json"
 	"flag"
-	"git.wanbits.io/joe/franklin/protos"
+	pb "git.wanbits.io/joe/franklin/protos"
 	"git.wanbits.io/joe/kettle/log"
 	"git.wanbits.io/joe/kettle/mac"
 	"git.wanbits.io/joe/kettle/mq/mqsvr"
-	"git.wanbits.io/joe/kettle/utl"
 	"git.wanbits.io/joe/kettle/rds"
+	"git.wanbits.io/joe/kettle/utl"
 	"os"
 	"strconv"
 	"strings"
@@ -36,7 +36,7 @@ func InitEnv(defConf, defTest string) {
 	GBootAt = time.Now().Format("2006-01-02 15:04:05")
 }
 
-func InitAll(c * protos.AppConf) {
+func InitAll(c *pb.AppConf) {
 	var err error
 	_, err = ConnectEtcd(c.Zoo)
 	utl.ErrPanic(err)
@@ -46,11 +46,15 @@ func InitAll(c * protos.AppConf) {
 	log.SetDefaultLogger(logger)
 
 	// redis
-	rdsjs, err := GEtcdc.Get(PathRedis(c))
+	rdskv, err := GEtcdc.Get(PathRedis(c))
 	utl.ErrPanic(err)
 
-	var redisConf protos.RedisConf
-	err = json.Unmarshal(rdsjs, &redisConf)
+	if len(rdskv.Kvs) <= 0 {
+		utl.ErrPanic(utl.ErrContainerEmpty)
+	}
+
+	var redisConf pb.RedisConf
+	err = json.Unmarshal(rdskv.Kvs[0].Value, &redisConf)
 	utl.ErrPanic(err)
 
 	GRds, err = rds.Connect(redisConf.Addrs[0], redisConf.Password, int(redisConf.Db))
@@ -60,11 +64,15 @@ func InitAll(c * protos.AppConf) {
 	mqjs, err := GEtcdc.Get(PathMq(c))
 	utl.ErrPanic(err)
 
-	var mqConf protos.NatsConf
-	err = json.Unmarshal(mqjs, &mqConf)
+	if len(mqjs.Kvs) <= 0 {
+		utl.ErrPanic(utl.ErrContainerEmpty)
+	}
+
+	var mqConf pb.NatsConf
+	err = json.Unmarshal(mqjs.Kvs[0].Value, &mqConf)
 	utl.ErrPanic(err)
 
 	clientId := strconv.FormatUint(c.Id, 10)
 	GMq, err = mqsvr.NewStan(mqConf.Addrs, mqConf.Username, mqConf.Password, mqConf.ClusterId, clientId, clientId)
 	utl.ErrPanic(err)
-}
+}

+ 4 - 0
comp/vars.go

@@ -1,6 +1,7 @@
 package comp
 
 import (
+	"git.wanbits.io/joe/kettle/htp"
 	"git.wanbits.io/joe/kettle/mq/mqsvr"
 	"git.wanbits.io/joe/kettle/utl"
 	"github.com/go-redis/redis"
@@ -11,4 +12,7 @@ var (
 
 	GRds *redis.Client //TODO: switch to cluster
 	GMq  *mqsvr.Stan
+	GEv  *mqsvr.Nats
+
+	GRpc *htp.GinServer
 )

+ 5 - 2
go.mod

@@ -3,10 +3,13 @@ module git.wanbits.io/joe/franklin
 go 1.15
 
 require (
-	git.wanbits.io/joe/kettle v0.0.4
-	git.wanbits.io/joe/nnet v0.0.4
+	git.wanbits.io/joe/kettle v0.0.9
+	git.wanbits.io/joe/nnet v0.0.5
+	github.com/coreos/etcd v3.3.13+incompatible
+	github.com/gin-gonic/gin v1.6.3
 	github.com/go-redis/redis v6.15.9+incompatible
 	github.com/golang/protobuf v1.4.3
+	github.com/mitchellh/mapstructure v1.1.2
 	github.com/spf13/viper v1.7.1
 	go.uber.org/zap v1.16.0
 	google.golang.org/protobuf v1.25.0

+ 21 - 0
go.sum

@@ -13,10 +13,20 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 git.wanbits.io/joe/kettle v0.0.4 h1:G2lJdIT+ABuV1WWeeciCpED1TTu4MRQSGVTkfcRHSn4=
 git.wanbits.io/joe/kettle v0.0.4/go.mod h1:hRebMdygaBk2ZSawrZUnB44ndJs/g8iqz3HqCKR72Gw=
+git.wanbits.io/joe/kettle v0.0.5 h1:PLj7llUP3lmUEKTi3XAoprD8BMniIRpcKcqNdsLTc8E=
+git.wanbits.io/joe/kettle v0.0.5/go.mod h1:hRebMdygaBk2ZSawrZUnB44ndJs/g8iqz3HqCKR72Gw=
+git.wanbits.io/joe/kettle v0.0.7 h1:LZFhxX6Gd44AZ3c0PiVdtrRmgyMgog6xscIqtc2Id0c=
+git.wanbits.io/joe/kettle v0.0.7/go.mod h1:hRebMdygaBk2ZSawrZUnB44ndJs/g8iqz3HqCKR72Gw=
+git.wanbits.io/joe/kettle v0.0.8 h1:8Ui7Q/dTftLp8sfuskvSvAPFQYGP9v1IpdPRnRhTC4M=
+git.wanbits.io/joe/kettle v0.0.8/go.mod h1:hRebMdygaBk2ZSawrZUnB44ndJs/g8iqz3HqCKR72Gw=
+git.wanbits.io/joe/kettle v0.0.9 h1:hFEfyMeBncuMCHSl9CQgLWxMvRfHPDBn1l1t8+h9Dkc=
+git.wanbits.io/joe/kettle v0.0.9/go.mod h1:hRebMdygaBk2ZSawrZUnB44ndJs/g8iqz3HqCKR72Gw=
 git.wanbits.io/joe/nnet v0.0.3 h1:bmY62xKxTFiJUx+zRMF7vetyVg8oHEishWEcQUqotX0=
 git.wanbits.io/joe/nnet v0.0.3/go.mod h1:lkxkmE61wr5HOsFQsuZqnqvsWVADYnTqt7Jhx3b36g8=
 git.wanbits.io/joe/nnet v0.0.4 h1:IS0VdmYwINUvx0nVXBlqJwvZVIyeDEU77X+rtuu4ORE=
 git.wanbits.io/joe/nnet v0.0.4/go.mod h1:lkxkmE61wr5HOsFQsuZqnqvsWVADYnTqt7Jhx3b36g8=
+git.wanbits.io/joe/nnet v0.0.5 h1:y+lzHH81zbpEZH3KFU4tsevptr36jJIIleYSGZ1o8C8=
+git.wanbits.io/joe/nnet v0.0.5/go.mod h1:lkxkmE61wr5HOsFQsuZqnqvsWVADYnTqt7Jhx3b36g8=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
@@ -53,9 +63,12 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
 github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
 github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
+github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
 github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@@ -64,9 +77,12 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
 github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
 github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
 github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
 github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
 github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
@@ -158,12 +174,14 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ -187,6 +205,7 @@ github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6Yf
 github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
 github.com/nats-io/jwt v1.1.0 h1:+vOlgtM0ZsF46GbmUoadq0/2rChNS45gtxHEa3H1gqM=
 github.com/nats-io/jwt v1.1.0/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M=
+github.com/nats-io/nats-server/v2 v2.1.9 h1:Sxr2zpaapgpBT9ElTxTVe62W+qjnhPcKY/8W5cnA/Qk=
 github.com/nats-io/nats-server/v2 v2.1.9/go.mod h1:9qVyoewoYXzG1ME9ox0HwkkzyYvnlBDugfR4Gg/8uHU=
 github.com/nats-io/nats.go v1.10.0 h1:L8qnKaofSfNFbXg0C5F71LdjPRnmQwSsA4ukmkt1TvY=
 github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE=
@@ -253,7 +272,9 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
 github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
 github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc=
 github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=

+ 23 - 2
mod/keys.go

@@ -13,6 +13,27 @@ const (
 )
 
 // index user by uid
-func kh_user_uid(uid uint64) string {
-	return fmt.Sprintf("%s:%s", pxUser, uid)
+func kh_user(userId uint64) string {
+	return fmt.Sprintf("%s:%v", pxUser, userId)
+}
+
+// user kicked
+func ks_kick(userId uint64) string {
+	return fmt.Sprintf("%s:kick:%v", pxRT, userId)
+}
+
+func kh_user_agent(userId uint64) string {
+	return fmt.Sprintf("%s:user_agent", pxRT)
+}
+
+func kz_agent_user(userId uint64) string {
+	return fmt.Sprintf("%s:agent_users", pxRT)
+}
+
+func kh_user_logic(userId uint64) string {
+	return fmt.Sprintf("%s:user_logic", pxRT)
+}
+
+func kh_logic_user(userId uint64) string {
+	return fmt.Sprintf("%s:logic_users", pxRT)
 }

+ 89 - 0
mod/mod.go

@@ -1 +1,90 @@
 package mod
+
+import (
+	pb "git.wanbits.io/joe/franklin/protos"
+	"git.wanbits.io/joe/kettle/utl"
+	"github.com/go-redis/redis"
+	"github.com/mitchellh/mapstructure"
+	"reflect"
+	"strings"
+	"time"
+)
+
+var (
+	rds *redis.Client
+)
+
+// convert ANY struct( or its pointer) to map[string]interface{}
+// opt: bool. if true convert keys of struct to their lowercase
+func StructToMap(stru interface{}, lowerCase ...bool) (map[string]interface{}, error) {
+	conf := func(v string) string {
+		return v
+	}
+	if len(lowerCase) > 0 && lowerCase[0] {
+		conf = strings.ToLower
+	}
+
+	t := reflect.TypeOf(stru)
+	vals := reflect.ValueOf(stru)
+	switch t.Kind() {
+	case reflect.Ptr:
+		t = t.Elem()
+		vals = vals.Elem()
+		if t.Kind() != reflect.Struct {
+			return nil, utl.ErrParameters
+		}
+	case reflect.Struct:
+	default:
+		return nil, utl.ErrParameters
+	}
+	rt := make(map[string]interface{})
+	for i := 0; i < t.NumField(); i++ {
+		fn := conf(t.Field(i).Name)
+		val := vals.Field(i).Interface()
+		rt[fn] = val
+	}
+	return rt, nil
+}
+
+func Install(client *redis.Client) {
+	rds = client
+}
+
+func ExistUser(userId uint64) bool {
+	ival, err := rds.Exists(kh_user(userId)).Result()
+	return err == nil && ival != 0
+}
+
+func LoadUser(userId uint64) (*pb.User, error) {
+	mp, err := rds.HGetAll(kh_user(userId)).Result()
+	if err != nil {
+		return nil, err
+	}
+	//mpsi := make(map[string]interface{})
+	//for k, v := range mp {
+	//	mpsi[k] = v
+	//}
+	user := &pb.User{}
+	err = mapstructure.Decode(mp, user)
+	return user, err
+}
+
+func UpdateUserAttr(userId uint64, field string, val interface{}) error {
+	_, err := rds.HSet(kh_user(userId), field, val).Result()
+	return err
+}
+
+func UpdateUserAttrs(userId uint64, fields map[string]interface{}) error {
+	_, err := rds.HMSet(kh_user(userId), fields).Result()
+	return err
+}
+
+func SaveKick(userId uint64, secs int) error {
+	_, err := rds.Set(ks_kick(userId), 1, time.Duration(secs)*time.Second).Result()
+	return err
+}
+
+func LoadKick(userId uint64) bool {
+	_, err := rds.Get(ks_kick(userId)).Result()
+	return err == nil
+}

+ 1 - 1
protos

@@ -1 +1 @@
-Subproject commit 57e8d09785905f942104944b7cbe052637b8bd7a
+Subproject commit 2fb8aee2381a1715b0dc050d4109ac28118656d4

+ 0 - 0
scripts/redis_lua/save_user_agent.lua


+ 1 - 0
scripts/redis_lua/user_change_logic.lua

@@ -0,0 +1 @@
+-- overwrite user-logic logic-user

+ 1 - 0
scripts/redis_lua/user_login.lua

@@ -0,0 +1 @@
+-- setup user-agent agent-user

+ 1 - 0
scripts/redis_lua/user_logout.lua

@@ -0,0 +1 @@
+-- delete user-agent, agent-user, user-logic, logic-user