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