package main import ( "encoding/json" "fmt" "html/template" "io" "log" "net/http" "strings" glws "git.alfi.li/gamelang/frontend/webserver" glwss "git.alfi.li/gamelang/frontend/wsserver" jeopardyman "git.alfi.li/gamelang/games/jeopardy/jeopardyManager" gamelangpb "git.alfi.li/gamelang/protobuf/gamelang" gljeopardypb "git.alfi.li/gamelang/protobuf/gamelang-jeopardy" userman "git.alfi.li/gamelang/systems/usermanager" worldman "git.alfi.li/gamelang/systems/worldmanager" "github.com/labstack/echo/v4" "gopkg.in/gcfg.v1" ) // Template wraps go's html template type Template struct { templates *template.Template } // Render renders a Template func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { return t.templates.ExecuteTemplate(w, name, data) } func initTemplates(e *echo.Echo) { t := &Template{ templates: template.Must(template.ParseGlob("public/*.html")), } e.Renderer = t } type JeopardyConfig struct { Http struct { ListenIP string ListenPort string } WS struct { ListenIP string ListenPort string } Manager struct { UserManager string WorldManager string JeopardyManager string } } func getDefaultConfig() JeopardyConfig { bc := JeopardyConfig{} bc.Http.ListenIP = "0.0.0.0" bc.Http.ListenPort = "8080" bc.WS.ListenIP = "0.0.0.0" bc.WS.ListenPort = "8081" bc.Manager.UserManager = "localhost:9090" bc.Manager.WorldManager = "localhost:9091" bc.Manager.JeopardyManager = "localhost:9092" return bc } type JeopardyGameServer struct { um *userman.UserManager //gamelangpb.UserServiceClient wm *worldman.WorldManager //gamelangpb.WorldServiceClient jeopardymanager *jeopardyman.JeopardyManager webserver *glws.Webserver wssserver *glwss.WSServer notichan chan gljeopardypb.Event config JeopardyConfig } func (bg *JeopardyGameServer) Init(config JeopardyConfig) { //init userclient //userconn, err := grpc.Dial(config.Manager.UserManager, grpc.WithInsecure()) //if err != nil { // panic(err.Error()) //} //defer userconn.Close() bg.um = userman.NewUserManager([]string{}) //gamelangpb.NewUserServiceClient(userconn) //init wm //worldconn, err := grpc.Dial(config.Manager.WorldManager, grpc.WithInsecure()) //if err != nil { // panic(err.Error()) //} //defer worldconn.Close() bg.wm = worldman.NewWorldManager([]string{}) //gamelangpb.NewWorldServiceClient(worldconn) bg.notichan = make(chan gljeopardypb.Event) bg.jeopardymanager = jeopardyman.NewJeopardyManager([]string{}, bg.notichan) go bg.workNotifications() //init webserver wsConfig := glws.Config{} wsConfig.Http.ListenIP = config.Http.ListenIP wsConfig.Http.ListenPort = config.Http.ListenPort bg.webserver = glws.NewWebserver(wsConfig) bg.wssserver = glwss.NewWSServer([]string{}, nil, bg.um) } func (bg *JeopardyGameServer) workNotifications() { for { log.Print("waiting for notification") event := <-bg.notichan log.Println("new notification", event) eventJson, err := json.Marshal(event) if err != nil { log.Printf("workNotifications - err marshalling event \"%s\"\n%s", event, err.Error()) } notify := glwss.WSMsg{Mtype: "game:event", Payload: string(eventJson)} worldgamename := strings.Split(event.Gamename, "\uffff") worldname := worldgamename[0] gamename := worldgamename[1] // if the game is a common game or its a win event all players have to be notified if gamename == "common" || event.Type == "Win" { world := gamelangpb.World{} worlds := bg.wm.List() for _, world := range worlds { if world.GetName() == worldname { log.Println("sending world\n", world) break } } //world, err := bg.wm.GetWorld(context.Background(), &gamelangpb.World{Name: worldname}) if err != nil { log.Printf("workNotifications - err getting world msg \"%s\"\n%s", worldname, err.Error()) return } for _, user := range world.Users { go bg.wssserver.Write(user.Name, notify) } } else { bg.wssserver.Write(event.Username, notify) } } } // RenderEndpoints returns the endpoints that are served by the webserver func (bg *JeopardyGameServer) RenderEndpoints() []glws.Endpoint { endpoints := []glws.Endpoint{} endpoints = append(endpoints, glws.Endpoint{"GET", "/", func(ctx echo.Context) error { log.Print("/: get") ok, user := bg.webserver.CheckSess(ctx) if !ok { log.Print("/: not found") return ctx.Redirect(301, "/login") } data := struct { Username string Worlds []gamelangpb.World Items []string }{ Username: user.Name, Worlds: func() []gamelangpb.World { worlds := bg.wm.List() return worlds }(), Items: []string{ "user", "world", }, } log.Printf("/: user \"%v\" found", user.Name) return ctx.Render(http.StatusOK, "index", data) }}) endpoints = append(endpoints, glws.Endpoint{"GET", "/admin", func(ctx echo.Context) error { log.Print("/admin: get") ok, user := bg.webserver.CheckSess(ctx) if !ok { log.Print("/admin: not logged in") return ctx.String(http.StatusUnauthorized, "not logged in") } log.Println("/admin:", user.Name) if user.Admin != true && user.Name != "admin" { log.Println("/admin: user is not an admin", user.Name) return ctx.String(http.StatusUnauthorized, "you are not an admin") } data := struct { Users []gamelangpb.User Sessions map[string]gamelangpb.User Worlds []gamelangpb.World }{ Users: func() []gamelangpb.User { users := bg.um.List() return users }(), Sessions: bg.webserver.ListSess(), Worlds: func() []gamelangpb.World { worlds := bg.wm.List() return worlds }(), } err := ctx.Render(http.StatusOK, "admin", data) if err != nil { log.Println(err) } return err }}) endpoints = append(endpoints, userEndpoints(bg)...) endpoints = append(endpoints, worldEndpoints(bg)...) // worldbuilder // Get to /World returns register form } //RenderHandler renders the endpoints that will be served via websocket func (bg *JeopardyGameServer) RenderHandler() []glwss.Handler { handler := []glwss.Handler{} handler = append(handler, glwss.Handler{MType: "echo", Callback: func(msg glwss.WSMsg, user gamelangpb.User) (glwss.WSMsg, error) { log.Printf("echo from \"%v\" with \"%s\"", user, msg.Payload) remsg := glwss.WSMsg{Mtype: "echo", Payload: msg.Payload} return remsg, nil }}) handler = append(handler, worldHandler()...) return handler } func CraftLobbygame(name string) gljeopardypb.Jeopardy { lobbygame := gljeopardypb.Jeopardy{Name: name} lobbygame.Options = append(lobbygame.Options, &gljeopardypb.Option{Key: "unique fields", Bool: true, Type: 1}) lobbygame.Options = append(lobbygame.Options, &gljeopardypb.Option{Key: "common game", Bool: true, Type: 1}) lobbygame.Numcols = 5 lobbygame.Numrows = 5 lobbygame.Textlist = []string{"word"} return lobbygame } func main() { var config JeopardyConfig err := gcfg.ReadFileInto(&config, "config") if err != nil { if strings.Count(err.Error(), "no such file or directory") > 0 { config = getDefaultConfig() } else { log.Fatal(err) } } log.Print("config read") log.Printf("%+v", config) jeopardygame := JeopardyGameServer{} jeopardygame.Init(config) jeopardygame.webserver.InitEndpoints(jeopardygame.RenderEndpoints()) go jeopardygame.webserver.Run() handler := jeopardygame.RenderHandler() keys := "" for _, k := range handler { keys = fmt.Sprintf("%s\n%v", keys, k) } log.Println("available MTypes:\n", keys, "\n----") jeopardygame.wssserver.RegisterHandler(handler) http.HandleFunc("/ws", jeopardygame.wssserver.WebsocketHandler) log.Println("websocket listening on", fmt.Sprintf("%s:%s", config.WS.ListenIP, config.WS.ListenPort)) log.Print(http.ListenAndServe(fmt.Sprintf("%s:%s", config.WS.ListenIP, config.WS.ListenPort), nil).Error()) }