func (handler *RequestHandler) HandleRequest(request []byte) []byte { command, requestJson := splitOnce(request) logger.Infof("Got command %s and request json %s", string(command), string(requestJson)) if handler.sessionGameWrapper == nil { if string(command) == api.COMMAND_NEW { response, game := newRequest(requestJson) handler.sessionGameWrapper = game logger.Infof("After new game request, game is now %t nil", (handler.sessionGameWrapper == nil)) return buildResponse(command, response) } else { return buildResponse(command, respondUnknownRequest("Need new game request")) } } else { fun, ok := handler.gameRequest[string(command)] if ok { playerId, requestJsonNoPlayerId := splitOnce(requestJson) playerIdInt, err := strconv.Atoi(string(playerId)) if err != nil { logger.Warnf("Not a playerId %s (%s)", playerId, err.Error()) return buildResponse(command, respondMalformed("playerId not an int")) } logger.Infof("request for playerId %d", playerIdInt) response := fun(requestJsonNoPlayerId, playerIdInt, handler.sessionGameWrapper) return buildResponse(command, response) } else { logger.Warnf("Unknown Command %s", string(command)) return buildResponse(command, respondUnknownRequest("Unknown command")) } } }
func (gameWrapper *GameWrapper) verifyValidMove(unitId int, player *game.Player, locations []game.Location) error { if len(locations) < 1 { message := "must supply more than zero locations" logger.Warnf(message) return errors.New(message) } tiles := make([]game.Terrain, len(locations)) terrain := gameWrapper.world.GetTerrain() for i, location := range locations { tiles[i] = terrain[location.GetX()][location.GetY()] } for _, location := range locations[1:] { err := gameWrapper.world.IsUnitAtLocation(location) if err != nil { logger.Warn(err) return errors.New(fmt.Sprintf("Cannot pass through units at (%d, %d)", location.GetX(), location.GetY())) } } unit, err := gameWrapper.world.GetAndVerifyOwnedUnit(player, unitId) if err != nil { return err } return game.ValidMove(unit.GetMovement().GetDistance(), unit.GetMovement(), tiles, locations) }
func CommandMarshal(cmd string, payload interface{}) []byte { body, err := json.Marshal(payload) if err != nil { logger.Warnf("Error marshalling json: ", err) return nil } return append([]byte(cmd+":"), body...) }
func (world *World) verifyOwnedUnit(player *Player, unit *Unit) error { if unit.nation != player.nation { logger.Warnf("Unit owned by %d is not owned by the current player (%d)", unit.nation, player.nation) return errors.New("Unit is not owned by the current player") } else { return nil } }
func (gh *game_hub) handleNewGame(message string, cconn *clientConnection) { ng := newGame{} err := json.Unmarshal([]byte(message), &ng) if err != nil { logger.Warnf("Error unmarshalling json: %s", err) return } ng.cconn = cconn logger.Infof("Got new game %s", ng) gh.gameRequests <- &ng }
func (gh *game_hub) handleWebsocket(message []byte, cconn *clientConnection) { cmds := strings.SplitN(string(message), ":", 2) if len(cmds) == 2 { if fun, ok := gh.localHandlers[cmds[0]]; ok { fun(cmds[1], cconn) } else { logger.Warnf("Unrecognized command: %s", cmds[0]) cconn.toClient <- []byte("unrecognized:") } } else { logger.Errorf("Malformed command: %s", cmds) } }
func handleConnection(netConn net.Conn) error { conn := connection.NewSocketConn(netConn) handler := request_handler.NewRequestHandler() for { logger.Infof("Handling a connection") request, err := conn.Read() if err != nil { logger.Warnf("Error reading request (%s)", err.Error()) conn.Close() return err } logger.Debugf("Got request %s", string(request)) response := handler.HandleRequest(request) logger.Debugf("Sent response %s", string(response)) err = conn.Write(response) if err != nil { logger.Warnf("Error writing response (%s)", err.Error()) conn.Close() return err } } }
func listenForever(port int) error { ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port)) if err != nil { panic(err) } for { conn, err := ln.Accept() if err != nil { logger.Warnf("Error accepting connection (%s)", err.Error()) continue } go handleConnection(conn) } }
func generateResponse(payload interface{}, status int) []byte { response, err := json.Marshal(api.ResponseType{Status: status, Payload: payload}) if err != nil { logger.Warnf("Could not generate response: %s", err.Error()) backupResponse, err := json.Marshal(api.ResponseType{ Status: api.STATUS_UNSERIALIZEABLE_RESPONSE, Payload: "problem"}) if err != nil { return []byte("Really bad") } logger.Infof("Generating response with status %d", status) logger.Debugf("Full message %s", string(response)) return backupResponse } logger.Infof("Generating response with status %d", status) logger.Debugf("Full message %s", string(response)) return response }
func (gh *game_hub) handleClientInfo(message string, cconn *clientConnection) { ci := clientInfo{} resp := response{0, nil} // I hate repeating this unmarshalling code, does Go allow something more general? err := json.Unmarshal([]byte(message), &ci) if err != nil { logger.Warnf("Error unmarshalling json: %s", err) return } userid, err := getClientIdFromToken(ci.Token) if err != nil { logger.Errorf("Error querying database: %s", err) resp.Status = -1 cconn.toClient <- CommandMarshal("clientInfo", resp) return } ci.id = userid cconn.info = ci cconn.toClient <- CommandMarshal("clientInfo", resp) }
func (world *World) AddUnit( location Location, name string, nation nation, health int, attacks []*attack, armor *armor, movement *Movement) error { logger.Infof("Adding unit at (x: %d, y: %d)", location.x, location.y) unitId := world.nextUnitId _, okUnitLocation := world.unitMap[location] _, okUnitId := world.units[unitId] if !(okUnitLocation && okUnitId) { newUnit := NewUnit( name, unitId, nation, health, attacks, armor, movement) world.unitMap[location] = newUnit world.units[unitId] = newUnit world.nextUnitId += 1 logger.Infof("Added unit with id %d at (x: %d, y: %d)", unitId, location.x, location.y) return nil } else { logger.Warnf("Failed to add unit with id %d at (x: %d, y: %d)", unitId, location.x, location.y) return errors.New("location already occupied") } }
func serveWs(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { http.Error(w, "Method not allowed", 405) return } if strings.Split(r.Header.Get("Origin"), ":")[1] != strings.Split("http://"+r.Host, ":")[1] { logger.Warnf("Cross origin problem: %s", r.Host) http.Error(w, "Origin not allowed", 403) return } ws, err := websocket.Upgrade(w, r, nil, 1024, 1024) if _, ok := err.(websocket.HandshakeError); ok { http.Error(w, "Not a websocket handshake", 400) return } else if err != nil { logger.Errorf("Websocket upgrade error: %s", err) return } ws.SetReadLimit(RECV_BUF_LEN) conn := NewWebsocketConn(ws) gamehub.connRegister <- conn }
func (gh *game_hub) processNewGameRequests() { for ng := range gh.gameRequests { // look for an existing game to satisfy the new request gm, err := gh.findGame(ng) // an error here implies duplicate player ids in game requests if err != nil { logger.Warnf("Player with id %d attempted a \"self\" game", ng.cconn.info.id) return } // create a game if one can't be found if gm == nil { logger.Info("Couldn't find an available game. Creating a new one") gm = gh.makeGame(ng.NumPlayers) } else { logger.Info("Found existing game. Slotting in") } gm.proxy.slotClientConnection(gm.currentPlayers, ng.cconn) gm.currentPlayers += 1 if gm.currentPlayers == gm.numPlayers { gh.commitGame(gm) } } }
func (world *World) MoveUnitFromTo(unitId int, start Location, end Location) error { unit, err := world.GetUnitAtLocation(start) if err != nil { return err } verifiedUnit, err := world.GetUnitFromId(unitId) if err != nil { return err } if unit != verifiedUnit { message := fmt.Sprintf("Unit with given unitId (%d) isn't at the specified location (%d, %d)", unitId, start.x, start.y) logger.Warnf(message) return errors.New(message) } unit.canMove = false err = world.IsUnitAtLocation(end) if err != nil { return err } else { world.unitMap[end] = unit delete(world.unitMap, start) return nil } }
func NewWorld(playerIds []int, worldId int) (*World, error) { db, err := newDatabase() if err != nil { logger.Errorf("DB is open and cannot be used (%s)", err.Error()) return nil, err } defer db.Close() terrains, err := loadTerrains(db) if err != nil { return nil, err } nations, err := loadNations(db) if err != nil { return nil, err } numPlayers := len(playerIds) if numPlayers > 4 || numPlayers < 1 { logger.Warnf("Must have between 1 and 4 players, got %d", numPlayers) return nil, errors.New("must have between 1 and 4 players") } if len(nations) < numPlayers { logger.Errorf("Not enough nations were loaded, must have at least 2, got %s", nations) return nil, errors.New("Not enough nations were loaded, must have at least 2") } players := make([]*Player, numPlayers) for i, playerId := range playerIds { players[i] = NewPlayer(playerId, nations[i], team(i)) } if len(terrains) < 2 { return nil, errors.New("No terrains were loadable") } plains := terrains[0] roads := terrains[1] ret_world := &World{ terrain: [][]Terrain{ []Terrain{plains, roads, plains, plains, plains, plains, plains, plains}, []Terrain{plains, roads, plains, plains, plains, plains, plains, plains}, []Terrain{roads, roads, roads, plains, plains, plains, plains, plains}, []Terrain{plains, plains, roads, plains, plains, plains, plains, plains}, []Terrain{plains, plains, roads, plains, plains, plains, plains, plains}, []Terrain{plains, plains, plains, plains, plains, plains, plains, plains}}, unitMap: make(map[Location]*Unit), units: make(map[int]*Unit), players: players, numPlayers: numPlayers, turnOwner: 0, nextUnitId: 0} if worldId == 0 { name := "warrior" dbHealth, dbAttacks, dbArmor, dbMovement, err := loadUnit(db, name) if err != nil { return nil, err } ret_world.AddUnit(NewLocation(0, 0), name, nations[0], dbHealth, dbAttacks, dbArmor, dbMovement) name = "mage" dbHealth, dbAttacks, dbArmor, dbMovement, err = loadUnit(db, name) if err != nil { return nil, err } ret_world.AddUnit(NewLocation(3, 3), name, nations[0], dbHealth, dbAttacks, dbArmor, dbMovement) if numPlayers == 2 { ret_world.AddUnit(NewLocation(0, 3), name, nations[1], dbHealth, dbAttacks, dbArmor, dbMovement) } } return ret_world, nil }