package core import ( "anvil/shared" "bytes" "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) { 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("
IP: %s
", ip) // // gs.Logger.Printf("Port: %s
", port) // // gs.Logger.Printf("Forwarded for: %s
", 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 }