Files
GameServer/shared/server.go

426 lines
10 KiB
Go
Raw Normal View History

2022-10-18 11:50:10 +09:00
package shared
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/signal"
"reflect"
"runtime/debug"
"strconv"
"strings"
"sync/atomic"
"syscall"
)
const (
// HTTPStatusReloginRequired : http status를 이걸 받으면 클라이언트는 로그아웃하고 로그인 화면으로 돌아가야 한다.
HTTPStatusReloginRequired = 599
// HTTPStatusReloginRequiredDupID :
HTTPStatusReloginRequiredDupID = 598
// HTTPStatusPlayerBlocked :
HTTPStatusPlayerBlocked = 597
)
type ShutdownFlag int32
const (
ShutdownFlagRunning = ShutdownFlag(0)
ShutdownFlagTerminating = ShutdownFlag(1)
ShutdownFlagRestarting = ShutdownFlag(2)
ShutdownFlagIdle = ShutdownFlag(3)
)
type functionCallContext struct {
Method string
Args []interface{}
}
// RPCReturnType : RPC 호출 가능한 함수가 유일하게 리턴할 수 있는 타입
type RPCReturnType interface {
Serialize(io.Writer) error
2022-11-10 20:41:41 +09:00
Value() interface{}
2022-10-18 11:50:10 +09:00
Error() error
}
type rpcReturnTypeImpl struct {
value reflect.Value
err error
serialized []byte
}
// Bytes : RPCReturnType.Serialize 구현
func (r rpcReturnTypeImpl) Serialize(w io.Writer) (err error) {
err = r.err
if err != nil {
return err
}
if len(r.serialized) > 0 {
w.Write(r.serialized)
return nil
}
if !r.value.IsValid() {
return nil
}
switch r.value.Kind() {
case reflect.String:
_, err = w.Write([]byte(r.value.String()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
_, err = w.Write([]byte(fmt.Sprintf("%d", r.value.Int())))
case reflect.Float32, reflect.Float64:
_, err = w.Write([]byte(fmt.Sprintf("%f", r.value.Float())))
case reflect.Slice:
switch r.value.Type().Elem().Kind() {
case reflect.Uint8:
_, err = w.Write(r.value.Bytes())
default:
var conv []interface{}
for i := 0; i < r.value.Len(); i++ {
conv = append(conv, r.value.Index(i).Interface())
}
if len(conv) == 0 {
_, err = w.Write([]byte("[]"))
} else {
err = json.NewEncoder(w).Encode(conv)
}
}
case reflect.Interface, reflect.Struct, reflect.Map:
err = json.NewEncoder(w).Encode(r.value.Interface())
case reflect.Ptr:
if !r.value.IsNil() {
err = json.NewEncoder(w).Encode(r.value.Interface())
}
}
return err
}
// Error : RPCReturnType.Error 구현
func (r rpcReturnTypeImpl) Error() error {
return r.err
}
2022-11-10 20:41:41 +09:00
// Error : RPCReturnType.Error 구현
func (r rpcReturnTypeImpl) Value() interface{} {
2022-12-19 15:36:27 +09:00
if r.value.IsValid() {
return r.value.Interface()
}
return nil
2022-11-10 20:41:41 +09:00
}
2022-10-18 11:50:10 +09:00
// MakeRPCReturn :
func MakeRPCReturn(value interface{}, err error) RPCReturnType {
return rpcReturnTypeImpl{
value: reflect.ValueOf(value),
err: err,
}
}
// MakeRPCReturnSerialized : 이미 시리얼라이즈 한 Return
func MakeRPCReturnSerialized(serialized []byte) RPCReturnType {
return rpcReturnTypeImpl{
serialized: serialized,
}
}
// MakeRPCError :
func MakeRPCError(args ...interface{}) RPCReturnType {
return rpcReturnTypeImpl{
value: reflect.ValueOf(nil),
err: errors.New(fmt.Sprint(args...)),
}
}
// MakeRPCErrorf :
func MakeRPCErrorf(format string, v ...interface{}) RPCReturnType {
return rpcReturnTypeImpl{
value: reflect.ValueOf(nil),
err: fmt.Errorf(format, v...),
}
}
// Server :
type Server struct {
httpserver *http.Server
shutdownFlag ShutdownFlag
preShutdown func()
stopfunc func()
}
var prefixptr = flag.String("prefix", "", "'")
var portptr = flag.Int("port", 80, "")
var tls = flag.String("tls", "", "")
func welcomeHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome"))
// welcome은 accesslog 안 남김
// if atomic.LoadInt32(&serveMux.Shutdowning) == 0 {
// w.Write([]byte("welcome"))
// } else {
// w.WriteHeader(http.StatusServiceUnavailable)
// }
io.Copy(ioutil.Discard, r.Body)
r.Body.Close()
}
func NewDependentServer(preShutdown func()) *Server {
server := &Server{
httpserver: nil,
shutdownFlag: ShutdownFlagIdle,
preShutdown: preShutdown,
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
go func() {
<-c
signal.Stop(c)
server.Shutdown(ShutdownFlagTerminating)
}()
return server
}
// NewHTTPServer :
func NewHTTPServer(serveMux *http.ServeMux, preShutdown func()) *Server {
server := NewDependentServer(preShutdown)
// 시작시 자동으로 enable됨
if len(*tls) > 0 && *portptr == 80 {
*portptr = 443
}
addr := fmt.Sprintf(":%d", *portptr)
serveMux.HandleFunc("/welcome", welcomeHandler)
server.httpserver = &http.Server{Addr: addr, Handler: serveMux}
server.httpserver.SetKeepAlivesEnabled(true)
return server
}
func (server *Server) ShutdownFlag() ShutdownFlag {
return ShutdownFlag(atomic.LoadInt32((*int32)(&server.shutdownFlag)))
}
// Shutdown :
func (server *Server) Shutdown(flag ShutdownFlag) {
atomic.StoreInt32((*int32)(&server.shutdownFlag), int32(flag))
if server.preShutdown != nil {
server.preShutdown()
}
if server.httpserver != nil {
server.httpserver.Shutdown(context.Background())
server.httpserver = nil
} else if server.stopfunc != nil {
server.stopfunc()
server.stopfunc = nil
}
}
// Start :
func (server *Server) Start() {
if !flag.Parsed() {
flag.Parse()
}
atomic.StoreInt32((*int32)(&server.shutdownFlag), int32(ShutdownFlagRunning))
if server.httpserver != nil {
var err error
if len(*tls) > 0 {
crtfile := *tls + ".crt"
keyfile := *tls + ".key"
err = server.httpserver.ListenAndServeTLS(crtfile, keyfile)
} else {
err = server.httpserver.ListenAndServe()
}
if err != http.ErrServerClosed {
fmt.Println(err)
}
} else {
ctx, stopfunc := context.WithCancel(context.Background())
server.stopfunc = stopfunc
<-ctx.Done()
}
}
// ConvertInterface :
func ConvertInterface(from interface{}, toType reflect.Type) reflect.Value {
if toType.Kind() == reflect.String {
return reflect.ValueOf(from.(string))
}
fromrv := reflect.ValueOf(from)
fromtype := reflect.TypeOf(from)
if toType.Kind() == reflect.Struct {
if frommap, ok := from.(map[string]interface{}); ok {
out := reflect.New(toType)
for k, v := range frommap {
if fieldval := out.Elem().FieldByName(k); fieldval.IsValid() {
fieldval.Set(ConvertInterface(v, fieldval.Type()))
}
}
return out
}
}
if fromtype.Kind() == reflect.Slice {
if fromtype.Elem() == toType.Elem() {
return fromrv
}
convslice := reflect.MakeSlice(toType, 0, fromrv.Len())
for i := 0; i < fromrv.Len(); i++ {
convslice = reflect.Append(convslice, ConvertInterface(fromrv.Index(i).Interface(), toType.Elem()))
}
return convslice
}
if toType.Kind() == reflect.Bool {
val, _ := strconv.ParseBool(from.(string))
return reflect.ValueOf(val)
}
return fromrv.Convert(toType)
}
// ErrUnmarshalRequestFailed :
var ErrUnmarshalRequestFailed = errors.New("Unmarshal failed at rpc handler")
2022-12-19 15:36:27 +09:00
var ErrInvalidContext = errors.New("Invalid context")
2022-10-18 11:50:10 +09:00
// CallMethodInternal :
func CallMethodInternal(receiver interface{}, context functionCallContext) (interface{}, error) {
methodTokens := strings.Split(context.Method, ".")
context.Method = methodTokens[len(methodTokens)-1]
for i := 0; i < len(methodTokens)-1; i++ {
token := methodTokens[i]
if strings.HasSuffix(token, "()") {
token = token[:len(token)-2]
}
fn := reflect.ValueOf(receiver).MethodByName(token)
// 이 fn은 인자를 받지 않고 하나만 리턴하는 함수여야 한다.
fnType := fn.Type()
if fnType.NumOut() != 1 || fnType.NumIn() != 0 {
return nil, errors.New(fmt.Sprint("method tokens are not correct :", token))
}
returnVals := fn.Call(nil)
if returnVals[0].IsNil() {
return nil, errors.New(fmt.Sprint("method token returns nil :", token))
}
receiver = returnVals[0].Interface()
}
fn := reflect.ValueOf(receiver).MethodByName(context.Method)
if fn.IsValid() {
fnType := fn.Type()
if fnType.NumOut() != 1 {
return nil, errors.New(fmt.Sprint("method should return only RPCReturnType :", context.Method))
}
if len(context.Args) != fnType.NumIn() {
return nil, errors.New(fmt.Sprint("argument is not matching :", context.Method))
}
var argv []reflect.Value
for i := 0; i < len(context.Args); i++ {
argv = append(argv, ConvertInterface(context.Args[i], fnType.In(i)))
}
returnVals := fn.Call(argv)
return returnVals[0].Interface(), nil
}
return nil, errors.New(fmt.Sprint("method is missing :", receiver, context.Method))
}
2022-12-19 15:36:27 +09:00
func CallMethodWithInterface(receiver interface{}, context interface{}) (retval RPCReturnType, err error) {
if context2, ok := context.(map[string]interface{}); ok {
var meta functionCallContext
if method, ok2 := context2["Method"].(string); ok2 {
meta.Method = method
} else {
return nil, ErrInvalidContext
}
if args, ok2 := context2["Args"].([]interface{}); ok2 {
meta.Args = args
} else {
return nil, ErrInvalidContext
}
if v, err := CallMethodInternal(receiver, meta); err == nil {
if cast, ok := v.(RPCReturnType); ok {
if cast.Error() != nil {
return nil, cast.Error()
}
return cast, nil
} else {
return nil, errors.New(fmt.Sprint("method should return only RPCReturnType", meta))
}
}
}
return nil, ErrInvalidContext
}
2022-10-18 11:50:10 +09:00
// CallMethod :
func CallMethod(receiver interface{}, context []byte) (retval RPCReturnType, err error) {
defer func() {
r := recover()
if r != nil && err == nil {
debug.PrintStack()
err = errors.New(fmt.Sprintf("%v", r))
}
}()
2022-12-19 15:36:27 +09:00
var raw interface{}
if err := json.Unmarshal(context, &raw); err != nil {
2022-10-18 11:50:10 +09:00
fmt.Println(string(context))
return nil, ErrUnmarshalRequestFailed
}
2022-12-19 15:36:27 +09:00
if meta, ok := raw.(map[string]interface{}); ok {
if methods, ok := meta["Methods"].([]interface{}); ok {
// calling cascaded methods
body := make([]interface{}, 0, len(methods))
2022-10-18 11:50:10 +09:00
2022-12-19 15:36:27 +09:00
for _, meta2 := range methods {
if cast, err := CallMethodWithInterface(receiver, meta2.(map[string]interface{})); err == nil {
body = append(body, cast.Value())
continue
}
return MakeRPCReturn(nil, err), err
}
return MakeRPCReturn(body, nil), nil
} else {
return CallMethodWithInterface(receiver, meta)
2022-10-18 11:50:10 +09:00
}
}
2022-12-19 15:36:27 +09:00
return nil, errors.New("Invalid context")
2022-10-18 11:50:10 +09:00
}