func (pool *sessionPoolImpl) OnSessionClose(f func(s Session)) {
	sf1 := reflect.ValueOf(f)
	for _, fun := range pool.SessionCloseCallbacks {
		sf2 := reflect.ValueOf(fun)
		if sf1.Pointer() == sf2.Pointer() {
			return
		}
	}
	pool.SessionCloseCallbacks = append(pool.SessionCloseCallbacks, f)
}
OnSessionClose adds a method that will be called when every session closes
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/v2/constants"
	"github.com/topfreegames/pitaya/v2/logger"
	"github.com/topfreegames/pitaya/v2/networkentity"
	"github.com/topfreegames/pitaya/v2/protos"
)
type sessionPoolImpl struct {
	sessionBindCallbacks []func(ctx context.Context, s Session) error
	afterBindCallbacks   []func(ctx context.Context, s Session) error
	SessionCloseCallbacks []func(s Session)
	sessionsByUID         sync.Map
	sessionsByID          sync.Map
	sessionIDSvc          *sessionIDService
	SessionCount int64
}
type SessionPool interface {
	NewSession(entity networkentity.NetworkEntity, frontend bool, UID ...string) Session
	GetSessionCount() int64
	GetSessionCloseCallbacks() []func(s Session)
	GetSessionByUID(uid string) Session
	GetSessionByID(id int64) Session
	OnSessionBind(f func(ctx context.Context, s Session) error)
	OnAfterSessionBind(f func(ctx context.Context, s Session) error)
	OnSessionClose(f func(s Session))
	CloseAll()
}
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 sessionImpl struct {
	sync.RWMutex                                   
	id                int64                        
	uid               string                       
	lastTime          int64                        
	entity            networkentity.NetworkEntity  
	data              map[string]interface{}       
	handshakeData     *HandshakeData               
	encodedData       []byte                       
	OnCloseCallbacks  []func()                     
	IsFrontend        bool                         
	frontendID        string                       
	frontendSessionID int64                        
	Subscriptions     []*nats.Subscription         
	pool              *sessionPoolImpl
}
type Session interface {
	GetOnCloseCallbacks() []func()
	GetIsFrontend() bool
	GetSubscriptions() []*nats.Subscription
	SetOnCloseCallbacks(callbacks []func())
	SetIsFrontend(isFrontend bool)
	SetSubscriptions(subscriptions []*nats.Subscription)
	Push(route string, v interface{}) error
	ResponseMID(ctx context.Context, mid uint, v interface{}, err ...bool) error
	ID() int64
	UID() string
	GetData() map[string]interface{}
	SetData(data map[string]interface{}) error
	GetDataEncoded() []byte
	SetDataEncoded(encodedData []byte) error
	SetFrontendData(frontendID string, frontendSessionID int64)
	Bind(ctx context.Context, uid string) error
	Kick(ctx context.Context) error
	OnClose(c func()) error
	Close()
	RemoteAddr() net.Addr
	Remove(key string) error
	Set(key string, value interface{}) error
	HasKey(key string) bool
	Get(key string) interface{}
	Int(key string) int
	Int8(key string) int8
	Int16(key string) int16
	Int32(key string) int32
	Int64(key string) int64
	Uint(key string) uint
	Uint8(key string) uint8
	Uint16(key string) uint16
	Uint32(key string) uint32
	Uint64(key string) uint64
	Float32(key string) float32
	Float64(key string) float64
	String(key string) string
	Value(key string) interface{}
	PushToFront(ctx context.Context) error
	Clear()
	SetHandshakeData(data *HandshakeData)
	GetHandshakeData() *HandshakeData
}
type sessionIDService struct {
	sid int64
}
func newSessionIDService() *sessionIDService {
	return &sessionIDService{
		sid: 0,
	}
}
func (c *sessionIDService) sessionID() int64 {
	return atomic.AddInt64(&c.sid, 1)
}
func (pool *sessionPoolImpl) NewSession(entity networkentity.NetworkEntity, frontend bool, UID ...string) Session {
	s := &sessionImpl{
		id:               pool.sessionIDSvc.sessionID(),
		entity:           entity,
		data:             make(map[string]interface{}),
		handshakeData:    nil,
		lastTime:         time.Now().Unix(),
		OnCloseCallbacks: []func(){},
		IsFrontend:       frontend,
		pool:             pool,
	}
	if frontend {
		pool.sessionsByID.Store(s.id, s)
		atomic.AddInt64(&pool.SessionCount, 1)
	}
	if len(UID) > 0 {
		s.uid = UID[0]
	}
	return s
}
func NewSessionPool() SessionPool {
	return &sessionPoolImpl{
		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),
		sessionIDSvc:          newSessionIDService(),
	}
}
func (pool *sessionPoolImpl) GetSessionCount() int64 {
	return pool.SessionCount
}
func (pool *sessionPoolImpl) GetSessionCloseCallbacks() []func(s Session) {
	return pool.SessionCloseCallbacks
}
func (pool *sessionPoolImpl) GetSessionByUID(uid string) Session {
	if val, ok := pool.sessionsByUID.Load(uid); ok {
		return val.(Session)
	}
	return nil
}
func (pool *sessionPoolImpl) GetSessionByID(id int64) Session {
	if val, ok := pool.sessionsByID.Load(id); ok {
		return val.(Session)
	}
	return nil
}
func (pool *sessionPoolImpl) OnSessionBind(f func(ctx context.Context, s Session) error) {
	sf1 := reflect.ValueOf(f)
	for _, fun := range pool.sessionBindCallbacks {
		sf2 := reflect.ValueOf(fun)
		if sf1.Pointer() == sf2.Pointer() {
			return
		}
	}
	pool.sessionBindCallbacks = append(pool.sessionBindCallbacks, f)
}
func (pool *sessionPoolImpl) OnAfterSessionBind(f func(ctx context.Context, s Session) error) {
	sf1 := reflect.ValueOf(f)
	for _, fun := range pool.afterBindCallbacks {
		sf2 := reflect.ValueOf(fun)
		if sf1.Pointer() == sf2.Pointer() {
			return
		}
	}
	pool.afterBindCallbacks = append(pool.afterBindCallbacks, f)
}