func (s *Session) ResponseMID(ctx context.Context, mid uint, v interface{}, err ...bool) error {
	return s.entity.ResponseMID(ctx, mid, v, err...)
}
ResponseMID responses message to client, mid is
request message ID
package session
import (
	"context"
	"encoding/json"
	"net"
	"reflect"
	"sync"
	"sync/atomic"
	"time"
	"github.com/golang/protobuf/proto"
	nats "github.com/nats-io/nats.go"
	"github.com/topfreegames/pitaya/constants"
	"github.com/topfreegames/pitaya/logger"
	"github.com/topfreegames/pitaya/protos"
)
type NetworkEntity interface {
	Push(route string, v interface{}) error
	ResponseMID(ctx context.Context, mid uint, v interface{}, isError ...bool) error
	Close() error
	Kick(ctx context.Context) error
	RemoteAddr() net.Addr
	SendRequest(ctx context.Context, serverID, route string, v interface{}) (*protos.Response, error)
}
var (
	sessionBindCallbacks = make([]func(ctx context.Context, s *Session) error, 0)
	afterBindCallbacks   = make([]func(ctx context.Context, s *Session) error, 0)
	SessionCloseCallbacks = make([]func(s *Session), 0)
	sessionsByUID         sync.Map                 
	sessionsByID          sync.Map                 
	sessionIDSvc          = newSessionIDService()  
	SessionCount int64
)
type HandshakeClientData struct {
	Platform    string `json:"platform"`
	LibVersion  string `json:"libVersion"`
	BuildNumber string `json:"clientBuildNumber"`
	Version     string `json:"clientVersion"`
}
type HandshakeData struct {
	Sys  HandshakeClientData    `json:"sys"`
	User map[string]interface{} `json:"user,omitempty"`
}
type Session struct {
	sync.RWMutex                              
	id                int64                   
	uid               string                  
	lastTime          int64                   
	entity            NetworkEntity           
	data              map[string]interface{}  
	handshakeData     *HandshakeData          
	encodedData       []byte                  
	OnCloseCallbacks  []func()                
	IsFrontend        bool                    
	frontendID        string                  
	frontendSessionID int64                   
	Subscriptions     []*nats.Subscription    
}
type sessionIDService struct {
	sid int64
}
func newSessionIDService() *sessionIDService {
	return &sessionIDService{
		sid: 0,
	}
}
func (c *sessionIDService) sessionID() int64 {
	return atomic.AddInt64(&c.sid, 1)  
}
func New(entity NetworkEntity, frontend bool, UID ...string) *Session {
	s := &Session{
		id:               sessionIDSvc.sessionID(),  
		entity:           entity,
		data:             make(map[string]interface{}),
		handshakeData:    nil,
		lastTime:         time.Now().Unix(),
		OnCloseCallbacks: []func(){},
		IsFrontend:       frontend,
	}
	if frontend {
		sessionsByID.Store(s.id, s)        
		atomic.AddInt64(&SessionCount, 1)  
	}
	if len(UID) > 0 {
		s.uid = UID[0]
	}
	return s
}
func GetSessionByUID(uid string) *Session {
	if val, ok := sessionsByUID.Load(uid); ok {
		return val.(*Session)
	}
	return nil
}
func GetSessionByID(id int64) *Session {
	if val, ok := sessionsByID.Load(id); ok {
		return val.(*Session)
	}
	return nil
}
func OnSessionBind(f func(ctx context.Context, s *Session) error) {
	sf1 := reflect.ValueOf(f)
	for _, fun := range sessionBindCallbacks {
		sf2 := reflect.ValueOf(fun)
		if sf1.Pointer() == sf2.Pointer() {
			return
		}
	}
	sessionBindCallbacks = append(sessionBindCallbacks, f)
}
func OnAfterSessionBind(f func(ctx context.Context, s *Session) error) {
	sf1 := reflect.ValueOf(f)
	for _, fun := range afterBindCallbacks {
		sf2 := reflect.ValueOf(fun)
		if sf1.Pointer() == sf2.Pointer() {
			return
		}
	}
	afterBindCallbacks = append(afterBindCallbacks, f)
}
func OnSessionClose(f func(s *Session)) {
	sf1 := reflect.ValueOf(f)
	for _, fun := range SessionCloseCallbacks {
		sf2 := reflect.ValueOf(fun)
		if sf1.Pointer() == sf2.Pointer() {
			return
		}
	}
	SessionCloseCallbacks = append(SessionCloseCallbacks, f)
}
func CloseAll() {
	logger.Log.Debugf("closing all sessions, %d sessions", SessionCount)
	sessionsByID.Range(func(_, value interface{}) bool {
		s := value.(*Session)
		s.Close()
		return true
	})
	logger.Log.Debug("finished closing sessions")
}
func (s *Session) updateEncodedData() error {
	var b []byte
	b, err := json.Marshal(s.data)
	if err != nil {
		return err
	}
	s.encodedData = b
	return nil
}
func (s *Session) Push(route string, v interface{}) error {
	return s.entity.Push(route, v)
}