func (pc *ProtoClient) Disconnect() {
	pc.Client.Disconnect()
	if pc.ready {
		pc.closeChan <- true
	}
}
Disconnect the client
package client
import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"errors"
	"io/ioutil"
	"strings"
	"time"
	"github.com/golang/protobuf/proto"
	protobuf "github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/jhump/protoreflect/desc"
	"github.com/jhump/protoreflect/dynamic"
	"github.com/sirupsen/logrus"
	"github.com/topfreegames/pitaya/conn/message"
	"github.com/topfreegames/pitaya/logger"
	"github.com/topfreegames/pitaya/protos"
)
type Command struct {
	input               string  
	output              string  
	inputMsgDescriptor  *desc.MessageDescriptor
	outputMsgDescriptor *desc.MessageDescriptor
}
type ProtoBufferInfo struct {
	Commands map[string]*Command
}
type ProtoClient struct {
	Client
	descriptorsNames        map[string]bool
	info                    ProtoBufferInfo
	docsRoute               string
	descriptorsRoute        string
	IncomingMsgChan         chan *message.Message
	expectedInputDescriptor *desc.MessageDescriptor
	ready                   bool
	closeChan               chan bool
}
func (pc *ProtoClient) MsgChannel() chan *message.Message {
	return pc.IncomingMsgChan
}
func unpackDescriptor(compressedDescriptor []byte) (*protobuf.FileDescriptorProto, error) {
	r, err := gzip.NewReader(bytes.NewReader(compressedDescriptor))
	if err != nil {
		return nil, err
	}
	defer r.Close()
	b, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	var fileDescriptorProto protobuf.FileDescriptorProto
	if err = proto.Unmarshal(b, &fileDescriptorProto); err != nil {
		return nil, err
	}
	return &fileDescriptorProto, nil
}
func (pc *ProtoClient) buildProtosFromDescriptor(descriptorArray []*protobuf.FileDescriptorProto) error {
	descriptorsMap := make(map[string]*desc.MessageDescriptor)
	descriptors, err := desc.CreateFileDescriptors(descriptorArray)
	if err != nil {
		return err
	}
	for name := range pc.descriptorsNames {
		for _, v := range descriptors {
			message := v.FindMessage(name)
			if message != nil {
				descriptorsMap[name] = message
			}
		}
	}
	for name, cmd := range pc.info.Commands {
		if msg, ok := descriptorsMap[cmd.input]; ok {
			pc.info.Commands[name].inputMsgDescriptor = msg
		}
		if msg, ok := descriptorsMap[cmd.output]; ok {
			pc.info.Commands[name].outputMsgDescriptor = msg
		}
	}
	return nil
}
func getOutputInputNames(command map[string]interface{}) (string, string, error) {
	outputName := ""
	inputName := ""
	in := command["input"]
	inputDocs, ok := in.(map[string]interface{})
	if ok {
		for k := range inputDocs {
			if strings.Contains(k, "proto") {
				inputName = strings.Replace(k, "*", "", 1)
			}
		}
	}
	out := command["output"]
	outputDocsArr := out.([]interface{})
	outputDocs, ok := outputDocsArr[0].(map[string]interface{})
	if ok {
		for k := range outputDocs {
			if strings.Contains(k, "proto") {
				outputName = strings.Replace(k, "*", "", 1)
			}
		}
	}
	return inputName, outputName, nil
}
func getKeys(info map[string]interface{}, keysSet map[string]bool) {
	for k, v := range info {
		if strings.Contains(k, "*") {
			kew := strings.Replace(k, "*", "", 1)
			keysSet[kew] = true
		}
		listofouts, ok := v.([]interface{})
		if ok {
			for i := range listofouts {
				aux, ok := listofouts[i].(map[string]interface{})
				if !ok {
					continue
				}
				getKeys(aux, keysSet)
			}
		}
		if aux, ok := v.(map[string]interface{}); ok {
			getKeys(aux, keysSet)
		}
	}
}
func (pc *ProtoClient) getDescriptors(data string) error {
	d := []byte(data)
	var jsonmap interface{}
	if err := json.Unmarshal(d, &jsonmap); err != nil {
		return err
	}
	m := jsonmap.(map[string]interface{})
	keysSet := make(map[string]bool)
	getKeys(m, keysSet)
	for _, commands := range pc.info.Commands {
		if commands.input != "" {
			keysSet[commands.input] = true
		}
		if commands.output != "" {
			keysSet[commands.output] = true
		}
	}
	handlers := m["handlers"].(map[string]interface{})
	for k, v := range handlers {
		cmdInfo := v.(map[string]interface{})
		in, out, err := getOutputInputNames(cmdInfo)
		if err != nil {
			return err
		}
		var command Command
		command.input = in
		command.output = out
		pc.info.Commands[k] = &command
		if pc.descriptorsRoute == "" && in == "protos.ProtoNames" && out == "protos.ProtoDescriptors" {
			pc.descriptorsRoute = k
		}
	}
	remotes := m["remotes"].(map[string]interface{})
	for k, v := range remotes {
		cmdInfo := v.(map[string]interface{})
		in, out, err := getOutputInputNames(cmdInfo)
		if err != nil {
			return err
		}
		var command Command
		command.input = in
		command.output = out
		pc.info.Commands[k] = &command
	}
	names := make([]string, 0, len(keysSet))
	for key := range keysSet {
		names = append(names, key)
	}
	protname := &protos.ProtoNames{
		Name: names,
	}
	encodedNames, err := proto.Marshal(protname)
	if err != nil {
		return err
	}
	_, err = pc.SendRequest(pc.descriptorsRoute, encodedNames)
	if err != nil {
		return err
	}
	response := <-pc.Client.IncomingMsgChan
	descriptors := &protos.ProtoDescriptors{}
	if err := proto.Unmarshal(response.Data, descriptors); err != nil {
		return err
	}
	descriptorArray := make([]*protobuf.FileDescriptorProto, 0)
	for i := range descriptors.Desc {
		fileDescriptorProto, err := unpackDescriptor(descriptors.Desc[i])
		if err != nil {
			return err
		}
		descriptorArray = append(descriptorArray, fileDescriptorProto)
		pc.descriptorsNames[names[i]] = true
	}
	if err = pc.buildProtosFromDescriptor(descriptorArray); err != nil {
		return err
	}
	return nil
}
func newProto(docslogLevel logrus.Level, requestTimeout ...time.Duration) *ProtoClient {
	return &ProtoClient{
		Client:           *New(docslogLevel, requestTimeout...),
		descriptorsNames: make(map[string]bool),
		info: ProtoBufferInfo{
			Commands: make(map[string]*Command),
		},
		docsRoute:        "",
		descriptorsRoute: "",
		IncomingMsgChan:  make(chan *message.Message, 10),
		closeChan:        make(chan bool),
	}
}
func NewProto(docsRoute string, docslogLevel logrus.Level, requestTimeout ...time.Duration) *ProtoClient {
	newclient := newProto(docslogLevel, requestTimeout...)
	newclient.docsRoute = docsRoute
	return newclient
}
func NewWithDescriptor(descriptorsRoute string, docsRoute string, docslogLevel logrus.Level, requestTimeout ...time.Duration) *ProtoClient {
	newclient := newProto(docslogLevel, requestTimeout...)
	newclient.docsRoute = docsRoute
	newclient.descriptorsRoute = descriptorsRoute
	return newclient
}
func (pc *ProtoClient) LoadServerInfo(addr string) error {
	pc.ready = false
	if err := pc.Client.ConnectToTLS(addr, true); err != nil {
		if err.Error() == "EOF" {
			if err := pc.Client.ConnectTo(addr); err != nil {
				return err
			}
		} else {
			return err
		}
	}
	_, err := pc.SendRequest(pc.docsRoute, make([]byte, 0))
	if err != nil {
		return err
	}
	response := <-pc.Client.IncomingMsgChan
	docs := &protos.Doc{}
	if err := proto.Unmarshal(response.Data, docs); err != nil {
		return err
	}
	if err := pc.getDescriptors(docs.Doc); err != nil {
		return err
	}
	pc.Disconnect()
	pc.ready = true
	return nil
}