first commit
						commit
						9d7de95373
					
				| @ -0,0 +1,17 @@ | ||||
| # Binaries for programs and plugins | ||||
| *.exe | ||||
| *.dll | ||||
| *.so | ||||
| *.dylib | ||||
| 
 | ||||
| # Test binary, build with `go test -c` | ||||
| *.test | ||||
| 
 | ||||
| # Output of the go coverage tool, specifically when used with LiteIDE | ||||
| *.out | ||||
| 
 | ||||
| # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 | ||||
| .glide/ | ||||
| 
 | ||||
| .idea/ | ||||
| .vscode/ | ||||
| @ -0,0 +1,14 @@ | ||||
| package component | ||||
| 
 | ||||
| var _ Component = (*Base)(nil) | ||||
| 
 | ||||
| // Base 空组件
 | ||||
| type Base struct{} | ||||
| 
 | ||||
| func (*Base) Init() {} | ||||
| 
 | ||||
| func (*Base) AfterInit() {} | ||||
| 
 | ||||
| func (*Base) BeforeShutdown() {} | ||||
| 
 | ||||
| func (*Base) Shutdown() {} | ||||
| @ -0,0 +1,16 @@ | ||||
| package component | ||||
| 
 | ||||
| // Component 具体组件的接口.
 | ||||
| type Component interface { | ||||
| 	// Init 初始化组件时调用.
 | ||||
| 	Init() | ||||
| 
 | ||||
| 	// AfterInit 初始化组件后调用.
 | ||||
| 	AfterInit() | ||||
| 
 | ||||
| 	// BeforeShutdown 组件被停止前调用.
 | ||||
| 	BeforeShutdown() | ||||
| 
 | ||||
| 	// Shutdown 停止组件时调用.
 | ||||
| 	Shutdown() | ||||
| } | ||||
| @ -0,0 +1,20 @@ | ||||
| package component | ||||
| 
 | ||||
| type CompWithOptions struct { | ||||
| 	Comp Component | ||||
| 	Opts []Option | ||||
| } | ||||
| 
 | ||||
| type Components struct { | ||||
| 	comps []CompWithOptions | ||||
| } | ||||
| 
 | ||||
| // Register registers a component to hub with options
 | ||||
| func (cs *Components) Register(c Component, options ...Option) { | ||||
| 	cs.comps = append(cs.comps, CompWithOptions{c, options}) | ||||
| } | ||||
| 
 | ||||
| // List returns all components with it's options
 | ||||
| func (cs *Components) List() []CompWithOptions { | ||||
| 	return cs.comps | ||||
| } | ||||
| @ -0,0 +1,55 @@ | ||||
| package component | ||||
| 
 | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"unicode" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	typeOfError = reflect.TypeOf((*error)(nil)).Elem() | ||||
| 	typeOfBytes = reflect.TypeOf(([]byte)(nil)) | ||||
| 	//typeOfSession = reflect.TypeOf(session.New(nil))
 | ||||
| ) | ||||
| 
 | ||||
| func isExported(name string) bool { | ||||
| 	w, _ := utf8.DecodeRuneInString(name) | ||||
| 	return unicode.IsUpper(w) | ||||
| } | ||||
| 
 | ||||
| func isExportedOrBuiltinType(t reflect.Type) bool { | ||||
| 	for t.Kind() == reflect.Ptr { | ||||
| 		t = t.Elem() | ||||
| 	} | ||||
| 	// PkgPath will be non-empty even for an exported type,
 | ||||
| 	// so we need to check the type name as well.
 | ||||
| 	return isExported(t.Name()) || t.PkgPath() == "" | ||||
| } | ||||
| 
 | ||||
| // isHandlerMethod decide a method is suitable handler method
 | ||||
| func isHandlerMethod(method reflect.Method) bool { | ||||
| 	mt := method.Type | ||||
| 	// Method must be exported.
 | ||||
| 	if method.PkgPath != "" { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// Method needs three ins: receiver, *Session, []byte or pointer.
 | ||||
| 	if mt.NumIn() != 3 { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// Method needs one outs: error
 | ||||
| 	if mt.NumOut() != 1 { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	if t1 := mt.In(1); t1.Kind() != reflect.Ptr || t1 != typeOfSession { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	if (mt.In(2).Kind() != reflect.Ptr && mt.In(2) != typeOfBytes) || mt.Out(0) != typeOfError { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| @ -0,0 +1,34 @@ | ||||
| package component | ||||
| 
 | ||||
| type ( | ||||
| 	options struct { | ||||
| 		name          string              // component name
 | ||||
| 		nameFunc      func(string) string // rename handler name
 | ||||
| 		schedulerName string              // schedulerName name
 | ||||
| 	} | ||||
| 
 | ||||
| 	// Option used to customize handler
 | ||||
| 	Option func(options *options) | ||||
| ) | ||||
| 
 | ||||
| // WithName used to rename component name
 | ||||
| func WithName(name string) Option { | ||||
| 	return func(opt *options) { | ||||
| 		opt.name = name | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithNameFunc override handler name by specific function
 | ||||
| // such as: strings.ToUpper/strings.ToLower
 | ||||
| func WithNameFunc(fn func(string) string) Option { | ||||
| 	return func(opt *options) { | ||||
| 		opt.nameFunc = fn | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithSchedulerName set the name of the service scheduler
 | ||||
| func WithSchedulerName(name string) Option { | ||||
| 	return func(opt *options) { | ||||
| 		opt.schedulerName = name | ||||
| 	} | ||||
| } | ||||
| @ -0,0 +1,107 @@ | ||||
| package component | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"reflect" | ||||
| ) | ||||
| 
 | ||||
| type ( | ||||
| 	// Handler represents a message.Message's handler's meta information.
 | ||||
| 	Handler struct { | ||||
| 		Receiver reflect.Value  // receiver of method
 | ||||
| 		Method   reflect.Method // method stub
 | ||||
| 		Type     reflect.Type   // arg type of method
 | ||||
| 		IsRawArg bool           // whether the data need to unserialize
 | ||||
| 	} | ||||
| 
 | ||||
| 	// Service implements a specific service, some of it's methods will be
 | ||||
| 	// called when the correspond events is occurred.
 | ||||
| 	Service struct { | ||||
| 		Name          string              // name of service
 | ||||
| 		Type          reflect.Type        // type of the receiver
 | ||||
| 		Receiver      reflect.Value       // receiver of methods for the service
 | ||||
| 		Handlers      map[string]*Handler // registered methods
 | ||||
| 		SchedulerName string              // name of scheduler variable in session data
 | ||||
| 		Options       options             // options
 | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| func NewService(comp Component, opts []Option) *Service { | ||||
| 	s := &Service{ | ||||
| 		Type:     reflect.TypeOf(comp), | ||||
| 		Receiver: reflect.ValueOf(comp), | ||||
| 	} | ||||
| 
 | ||||
| 	// apply options
 | ||||
| 	for i := range opts { | ||||
| 		opt := opts[i] | ||||
| 		opt(&s.Options) | ||||
| 	} | ||||
| 	if name := s.Options.name; name != "" { | ||||
| 		s.Name = name | ||||
| 	} else { | ||||
| 		s.Name = reflect.Indirect(s.Receiver).Type().Name() | ||||
| 	} | ||||
| 	s.SchedulerName = s.Options.schedulerName | ||||
| 
 | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| // suitableMethods returns suitable methods of typ
 | ||||
| func (s *Service) suitableHandlerMethods(typ reflect.Type) map[string]*Handler { | ||||
| 	methods := make(map[string]*Handler) | ||||
| 	for m := 0; m < typ.NumMethod(); m++ { | ||||
| 		method := typ.Method(m) | ||||
| 		mt := method.Type | ||||
| 		mn := method.Name | ||||
| 		if isHandlerMethod(method) { | ||||
| 			raw := false | ||||
| 			if mt.In(2) == typeOfBytes { | ||||
| 				raw = true | ||||
| 			} | ||||
| 			// rewrite handler name
 | ||||
| 			if s.Options.nameFunc != nil { | ||||
| 				mn = s.Options.nameFunc(mn) | ||||
| 			} | ||||
| 			methods[mn] = &Handler{Method: method, Type: mt.In(2), IsRawArg: raw} | ||||
| 		} | ||||
| 	} | ||||
| 	return methods | ||||
| } | ||||
| 
 | ||||
| // ExtractHandler extract the set of methods from the
 | ||||
| // receiver value which satisfy the following conditions:
 | ||||
| // - exported method of exported type
 | ||||
| // - two arguments, both of exported type
 | ||||
| // - the first argument is *session.Session
 | ||||
| // - the second argument is []byte or a pointer
 | ||||
| func (s *Service) ExtractHandler() error { | ||||
| 	typeName := reflect.Indirect(s.Receiver).Type().Name() | ||||
| 	if typeName == "" { | ||||
| 		return errors.New("no service name for type " + s.Type.String()) | ||||
| 	} | ||||
| 	if !isExported(typeName) { | ||||
| 		return errors.New("type " + typeName + " is not exported") | ||||
| 	} | ||||
| 
 | ||||
| 	// Install the methods
 | ||||
| 	s.Handlers = s.suitableHandlerMethods(s.Type) | ||||
| 
 | ||||
| 	if len(s.Handlers) == 0 { | ||||
| 		str := "" | ||||
| 		// To help the user, see if a pointer receiver would work.
 | ||||
| 		method := s.suitableHandlerMethods(reflect.PtrTo(s.Type)) | ||||
| 		if len(method) != 0 { | ||||
| 			str = "type " + s.Name + " has no exported methods of suitable type (hint: pass a pointer to value of that type)" | ||||
| 		} else { | ||||
| 			str = "type " + s.Name + " has no exported methods of suitable type" | ||||
| 		} | ||||
| 		return errors.New(str) | ||||
| 	} | ||||
| 
 | ||||
| 	for i := range s.Handlers { | ||||
| 		s.Handlers[i].Receiver = s.Receiver | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| @ -0,0 +1,124 @@ | ||||
| package codec | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"ng/internal/packet" | ||||
| ) | ||||
| 
 | ||||
| // Codec constants.
 | ||||
| const ( | ||||
| 	HeadLength    = 4 | ||||
| 	MaxPacketSize = 64 * 1024 | ||||
| ) | ||||
| 
 | ||||
| // ErrPacketSizeExceed is the error used for encode/decode.
 | ||||
| var ErrPacketSizeExceed = errors.New("codec: packet size exceed") | ||||
| 
 | ||||
| // A Decoder reads and decodes network data slice
 | ||||
| type Decoder struct { | ||||
| 	buf  *bytes.Buffer | ||||
| 	size int  // last packet length
 | ||||
| 	typ  byte // last packet type
 | ||||
| } | ||||
| 
 | ||||
| // NewDecoder returns a new decoder that used for decode network bytes slice.
 | ||||
| func NewDecoder() *Decoder { | ||||
| 	return &Decoder{ | ||||
| 		buf:  bytes.NewBuffer(nil), | ||||
| 		size: -1, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *Decoder) forward() error { | ||||
| 	header := c.buf.Next(HeadLength) | ||||
| 	c.typ = header[0] | ||||
| 	if c.typ < packet.Handshake || c.typ > packet.Kick { | ||||
| 		return packet.ErrWrongPacketType | ||||
| 	} | ||||
| 	c.size = bytesToInt(header[1:]) | ||||
| 
 | ||||
| 	// packet length limitation
 | ||||
| 	if c.size > MaxPacketSize { | ||||
| 		return ErrPacketSizeExceed | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Decode decode the network bytes slice to packet.Packet(s)
 | ||||
| // TODO(Warning): shared slice
 | ||||
| func (c *Decoder) Decode(data []byte) ([]*packet.Packet, error) { | ||||
| 	c.buf.Write(data) | ||||
| 
 | ||||
| 	var ( | ||||
| 		packets []*packet.Packet | ||||
| 		err     error | ||||
| 	) | ||||
| 	// check length
 | ||||
| 	if c.buf.Len() < HeadLength { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// first time
 | ||||
| 	if c.size < 0 { | ||||
| 		if err = c.forward(); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for c.size <= c.buf.Len() { | ||||
| 		p := &packet.Packet{Type: packet.Type(c.typ), Length: c.size, Data: c.buf.Next(c.size)} | ||||
| 		packets = append(packets, p) | ||||
| 
 | ||||
| 		// more packet
 | ||||
| 		if c.buf.Len() < HeadLength { | ||||
| 			c.size = -1 | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		if err = c.forward(); err != nil { | ||||
| 			return packets, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return packets, nil | ||||
| } | ||||
| 
 | ||||
| // Encode create a packet.Packet from  the raw bytes slice and then encode to network bytes slice
 | ||||
| // Protocol refs: https://github.com/NetEase/pomelo/wiki/Communication-Protocol
 | ||||
| //
 | ||||
| // -<type>-|--------<length>--------|-<data>-
 | ||||
| // --------|------------------------|--------
 | ||||
| // 1 byte packet type, 3 bytes packet data length(big end), and data segment
 | ||||
| func Encode(typ packet.Type, data []byte) ([]byte, error) { | ||||
| 	if typ < packet.Handshake || typ > packet.Kick { | ||||
| 		return nil, packet.ErrWrongPacketType | ||||
| 	} | ||||
| 
 | ||||
| 	p := &packet.Packet{Type: typ, Length: len(data)} | ||||
| 	buf := make([]byte, p.Length+HeadLength) | ||||
| 	buf[0] = byte(p.Type) | ||||
| 
 | ||||
| 	copy(buf[1:HeadLength], intToBytes(p.Length)) | ||||
| 	copy(buf[HeadLength:], data) | ||||
| 
 | ||||
| 	return buf, nil | ||||
| } | ||||
| 
 | ||||
| // Decode packet data length byte to int(Big end)
 | ||||
| func bytesToInt(b []byte) int { | ||||
| 	result := 0 | ||||
| 	for _, v := range b { | ||||
| 		result = result<<8 + int(v) | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
| 
 | ||||
| // Encode packet data length to bytes(Big end)
 | ||||
| func intToBytes(n int) []byte { | ||||
| 	buf := make([]byte, 3) | ||||
| 	buf[0] = byte((n >> 16) & 0xFF) | ||||
| 	buf[1] = byte((n >> 8) & 0xFF) | ||||
| 	buf[2] = byte(n & 0xFF) | ||||
| 	return buf | ||||
| } | ||||
| @ -0,0 +1,27 @@ | ||||
| package codec | ||||
| 
 | ||||
| import ( | ||||
| 	. "ng/internal/packet" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| func BenchmarkDecoder_Decode(b *testing.B) { | ||||
| 	data := []byte("hello world") | ||||
| 	pp1, err := Encode(Handshake, data) | ||||
| 	if err != nil { | ||||
| 		b.Error(err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	d1 := NewDecoder() | ||||
| 	b.ResetTimer() | ||||
| 	b.ReportAllocs() | ||||
| 	for i := 0; i < b.N; i++ { | ||||
| 		packets, err := d1.Decode(pp1) | ||||
| 		if err != nil { | ||||
| 			b.Fatal(err) | ||||
| 		} | ||||
| 		if len(packets) != 1 { | ||||
| 			b.Fatal("decode error") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -0,0 +1,33 @@ | ||||
| package log | ||||
| 
 | ||||
| import ( | ||||
| 	"log" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| // Logger represents  the log interface
 | ||||
| type Logger interface { | ||||
| 	Println(v ...interface{}) | ||||
| 	Fatal(v ...interface{}) | ||||
| 	Fatalf(format string, v ...interface{}) | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	SetLogger(log.New(os.Stderr, "[NGServer]", log.LstdFlags|log.Lshortfile)) | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	Println func(v ...interface{}) | ||||
| 	Fatal   func(v ...interface{}) | ||||
| 	Fatalf  func(format string, v ...interface{}) | ||||
| ) | ||||
| 
 | ||||
| // SetLogger rewrites the default logger
 | ||||
| func SetLogger(logger Logger) { | ||||
| 	if logger == nil { | ||||
| 		return | ||||
| 	} | ||||
| 	Println = logger.Println | ||||
| 	Fatal = logger.Fatal | ||||
| 	Fatalf = logger.Fatalf | ||||
| } | ||||
| @ -0,0 +1,47 @@ | ||||
| package packet | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| ) | ||||
| 
 | ||||
| // Type represents the network packet's type such as: handshake and so on.
 | ||||
| type Type byte | ||||
| 
 | ||||
| const ( | ||||
| 	_ Type = iota | ||||
| 	// Handshake represents a handshake: request(client) <====> handshake response(server)
 | ||||
| 	Handshake = 0x01 | ||||
| 
 | ||||
| 	// HandshakeAck represents a handshake ack from client to server
 | ||||
| 	HandshakeAck = 0x02 | ||||
| 
 | ||||
| 	// Heartbeat represents a heartbeat
 | ||||
| 	Heartbeat = 0x03 | ||||
| 
 | ||||
| 	// Data represents a common data packet
 | ||||
| 	Data = 0x04 | ||||
| 
 | ||||
| 	// Kick represents a kick off packet
 | ||||
| 	Kick = 0x05 // disconnect message from server
 | ||||
| ) | ||||
| 
 | ||||
| // ErrWrongPacketType represents a wrong packet type.
 | ||||
| var ErrWrongPacketType = errors.New("wrong packet type") | ||||
| 
 | ||||
| // Packet represents a network packet.
 | ||||
| type Packet struct { | ||||
| 	Type   Type | ||||
| 	Length int | ||||
| 	Data   []byte | ||||
| } | ||||
| 
 | ||||
| // New create a Packet instance.
 | ||||
| func New() *Packet { | ||||
| 	return &Packet{} | ||||
| } | ||||
| 
 | ||||
| // String represents the Packet's in text mode.
 | ||||
| func (p *Packet) String() string { | ||||
| 	return fmt.Sprintf("Type: %d, Length: %d, Data: %s", p.Type, p.Length, string(p.Data)) | ||||
| } | ||||
					Loading…
					
					
				
		Reference in New Issue