first commit

main
NorthLan 2 years ago
commit 9d7de95373

17
.gitignore vendored

@ -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,3 @@
module ng
go 1.18

@ -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…
Cancel
Save