2022-10-18 11:50:10 +09:00
|
|
|
package core
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"anvil/shared"
|
|
|
|
|
"bytes"
|
2022-12-14 14:57:09 +09:00
|
|
|
"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) {
|
2022-12-14 14:57:09 +09:00
|
|
|
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
|
|
|
|
|
}
|