Files
GameServer/core/AeGameServer.go

371 lines
7.5 KiB
Go
Raw Normal View History

2022-10-18 11:50:10 +09:00
package core
import (
"anvil/shared"
"bytes"
"flag"
2022-10-18 11:50:10 +09:00
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"path"
"runtime/debug"
"sync"
"time"
"github.com/gorilla/websocket"
)
// Warehouse :
type AeGameServer struct {
version string
fullversion string
Logger *log.Logger
server *shared.Server
mongoClient shared.MongoClient
WorldIp net.IP
WorldPort int32
LobbyIp net.IP
LobbyPort int32
sockets []*SocketContext
lock sync.Mutex
}
type transaction struct {
gs *AeGameServer
w http.ResponseWriter
r *http.Request
}
type SocketContext struct {
connection *websocket.Conn
gs *AeGameServer
Type int32
Port int32
Ip net.IP
Version string
lock sync.Mutex
}
// type category struct {
// gs *TestTest
// name string
// }
// type constCategory struct {
// category
// w http.ResponseWriter
// r *http.Request
// }
// Version :
func (gs *AeGameServer) Version() string {
return gs.version
}
// New :
func New() (*AeGameServer, error) {
if !flag.Parsed() {
flag.Parse()
}
2022-10-18 11:50:10 +09:00
logfilename := fmt.Sprintf("AEGS_%s.log", time.Now().Format("2006-01-02T15-04-05"))
logFile, err := os.OpenFile(logfilename, os.O_CREATE|os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
panic(err)
}
w := &AeGameServer{
version: "0.0.1",
fullversion: "0.0.1.0",
Logger: log.New(io.MultiWriter(logFile, os.Stdout), "[AEGS]", log.LstdFlags),
}
return w, nil
}
// IsRunning :
func (gs *AeGameServer) IsRunning() bool {
return gs.server.ShutdownFlag() == shared.ShutdownFlagRunning
}
// Start :
func (gs *AeGameServer) Start(serveMux *http.ServeMux) error {
http.DefaultClient.Timeout = time.Minute * 1
gs.sockets = make([]*SocketContext, 0, 10)
if serveMux != nil {
// 외부 serveMux가 있을 때는 registerHandler를 한번만 해야한다.
gs.registerHandlers(serveMux, "AeGameServer")
gs.server = shared.NewDependentServer(nil)
}
printStartErr := false
for {
if serveMux == nil {
serveMux = http.NewServeMux()
gs.registerHandlers(serveMux, "")
gs.server = shared.NewHTTPServer(serveMux, nil)
}
err := gs.startimpl()
if err != nil {
if !printStartErr {
printStartErr = true
fmt.Println(err)
}
time.Sleep(time.Second)
continue
}
printStartErr = false
for {
sf := gs.server.ShutdownFlag()
if sf == shared.ShutdownFlagTerminating {
return err
} else if sf == shared.ShutdownFlagIdle {
time.Sleep(time.Second)
} else if sf == shared.ShutdownFlagRestarting {
break
}
}
}
}
func (gs *AeGameServer) startimpl() (err error) {
gs.Logger.Println("creating MongoClient...")
gs.mongoClient, err = shared.NewMongoClient(&shared.ConnectionInfo{
Url: "mongodb://192.168.8.94:27017/?replicaSet=repl01",
Database: "AEtest",
})
if err != nil {
gs.Logger.Println("failed to create MongoClient. shutting down.")
gs.server.Shutdown(shared.ShutdownFlagTerminating)
return err
}
defer gs.mongoClient.Close()
gs.server.Start()
return nil
}
func (gs *AeGameServer) Shutdown(flag shared.ShutdownFlag) {
gs.server.Shutdown(flag)
}
func (gs *AeGameServer) registerHandlers(serveMux *http.ServeMux, prefix string) error {
if len(prefix) > 0 && prefix[0] != '/' {
prefix = "/" + prefix
}
if len(prefix) > 0 && prefix[len(prefix)-1] == '/' {
prefix = prefix[0 : len(prefix)-1]
}
if len(prefix) == 0 {
prefix = "/"
}
var upgrader = websocket.Upgrader{} // use default options
serveMux.HandleFunc(prefix, func(w http.ResponseWriter, r *http.Request) {
defer func() {
s := recover()
if s != nil {
debug.PrintStack()
gs.Logger.Println(s)
}
io.Copy(ioutil.Discard, r.Body)
r.Body.Close()
}()
if r.ContentLength == 0 {
w.Write([]byte("no content"))
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
tx := &transaction{
gs: gs,
w: w,
r: r,
}
if retval, err := shared.CallMethod(tx, body); err != nil {
gs.Logger.Println("CallMethod failed : ", r.RemoteAddr, string(body), r.Header, err)
w.WriteHeader(http.StatusInternalServerError)
} else {
retval.Serialize(w)
}
})
serveMux.HandleFunc(path.Join(prefix, "ws"), func(w http.ResponseWriter, r *http.Request) {
gs.lock.Lock()
defer gs.lock.Unlock()
defer func() {
s := recover()
if s != nil {
debug.PrintStack()
gs.Logger.Println(s)
}
io.Copy(ioutil.Discard, r.Body)
r.Body.Close()
}()
conn, err := upgrader.Upgrade(w, r, nil)
st := &SocketContext{
gs: gs,
connection: conn,
}
gs.sockets = append(gs.sockets, st)
if err != nil {
gs.Logger.Printf("upgrader.Upgrade: %v", err)
return
}
go func() {
for {
_, body, err := st.connection.ReadMessage()
if err != nil {
gs.Logger.Printf("conn.ReadMessage: %v", err)
func() {
gs.lock.Lock()
defer gs.lock.Unlock()
for i, s := range gs.sockets {
if st == s {
gs.sockets[i] = gs.sockets[len(gs.sockets)-1]
gs.sockets = gs.sockets[:len(gs.sockets)-1]
break
}
}
}()
st.connection.Close()
return
} else {
gs.Logger.Printf("msg from socket %s %s %s", st.connection.LocalAddr(), st.connection.RemoteAddr(), body)
if retval, err := shared.CallMethod(st, body); err != nil {
gs.Logger.Println("CallMethod failed : ", r.RemoteAddr, string(body), r.Header, err)
w.WriteHeader(http.StatusInternalServerError)
} else {
var b bytes.Buffer
retval.Serialize(&b)
st.connection.WriteMessage(websocket.TextMessage, b.Bytes())
}
}
}
}()
})
// categoryHandler := func(name string, receiver interface{}, w http.ResponseWriter, r *http.Request) {
// defer func() {
// s := recover()
// if s != nil {
// debug.PrintStack()
// gs.Logger.Println(s)
// }
// io.Copy(ioutil.Discard, r.Body)
// r.Body.Close()
// }()
// // ip, port, err := net.SplitHostPort(r.RemoteAddr)
// // if err != nil {
// // //return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)
// // gs.Logger.Printf("userip: %q is not IP:port", r.RemoteAddr)
// // }
// // userIP := net.ParseIP(ip)
// // if userIP == nil {
// // //return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)
// // gs.Logger.Printf("userip: %q is not IP:port", r.RemoteAddr)
// // return
// // }
// // // This will only be defined when site is accessed via non-anonymous proxy
// // // and takes precedence over RemoteAddr
// // // Header.Get is case-insensitive
// // forward := r.Header.Get("X-Forwarded-For")
// // gs.Logger.Printf("<p>IP: %s</p>", ip)
// // gs.Logger.Printf("<p>Port: %s</p>", port)
// // gs.Logger.Printf("<p>Forwarded for: %s</p>", forward)
// if r.ContentLength == 0 {
// w.Write([]byte("no content"))
// return
// }
// body, err := ioutil.ReadAll(r.Body)
// if err != nil {
// w.WriteHeader(http.StatusInternalServerError)
// }
// if len(body) == 0 {
// w.Write([]byte("no body"))
// gs.Logger.Println("no body")
// return
// }
// if retval, err := shared.CallMethod(receiver, body); err != nil {
// gs.Logger.Println("CallMethod failed : ", r.RemoteAddr, string(body), r.Header, err)
// } else {
// retval.Serialize(w)
// }
// }
// serveMux.HandleFunc(prefix+"/const", func(w http.ResponseWriter, r *http.Request) {
// // 읽기 전용
// constcat := constCategory{
// category: category{
// gs: gs,
// name: "const",
// },
// w: w,
// r: r,
// }
// categoryHandler(constcat.name, &constcat, w, r)
// })
return nil
}