Example #1
0
func (self *Game) Save(c common.Context) *Game {
	var err error
	if self.Id == nil {
		self.setPlayerNames(c)
		err = common.Transaction(c, func(c common.Context) (err error) {
			self.CreatedAt = time.Now()
			self.State = StateCreated
			self.Length = 1
			self.Id, err = datastore.Put(c, datastore.NewKey(c, GameKind, "", 0, nil), self)
			if err != nil {
				return
			}
			playerIds := make([]state.PlayerId, 0, len(self.Players))
			for _, id := range self.Players {
				playerIds = append(playerIds, state.PlayerId(id.Encode()))
			}
			turn := &Turn{
				State: state.RandomState(common.GAELogger{c}, playerIds),
			}
			turn.Save(c, self.Id)
			self.Turns = Turns{*turn}
			nextTurnFunc.Call(c, self.Id, self.PlayerNames)
			return nil
		})
		if err == nil {
			for _, playerId := range self.Players {
				common.Transaction(c, func(common.Context) error {
					if ai := GetAIById(c, playerId); ai != nil {
						ai.Games += 1
						ai.Save(c)
					}
					return nil
				})
			}
		}
	} else {
		_, err = datastore.Put(c, self.Id, self)
	}
	common.AssertOkError(err)
	common.MemDel(c, allGamesKey, gameKeyForId(self.Id))
	return self.process(c)
}
Example #2
0
func nextTurn(cont appengine.Context, id *datastore.Key, playerNames []string) {
	con := common.Context{Context: cont}
	self := getGameById(con, id)
	self.PlayerNames = playerNames
	if self.Length > maxGameDuration {
		self.State = StateFinished
		self.Save(con)
		con.Infof("Ended %v due to timeout", self.Id)
		return
	}
	errorSavers := []func(){}
	if err := common.Transaction(con, func(c common.Context) (err error) {
		lastTurn := GetLatestTurnByParent(c, self.Id)
		responses := make(chan orderResponse, len(self.Players))
		for _, playerId := range self.Players {
			orderResp := orderResponse{
				DatastorePlayerId: playerId,
				StatePlayerId:     state.PlayerId(playerId.Encode()),
			}
			if foundAi := GetAIById(c, playerId); foundAi != nil {
				go func() {
					// Always deliver the order response
					defer func() {
						responses <- orderResp
					}()

					// create a request
					orderRequest := ai.OrderRequest{
						Me:     orderResp.StatePlayerId,
						State:  lastTurn.State,
						GameId: state.GameId(self.Id.Encode()),
					}

					// encode it into a body, and remember its string representation
					sendBody := &bytes.Buffer{}
					aiCommon.MustEncodeJSON(sendBody, orderRequest)
					sendBodyString := sendBody.String()

					// get a client
					client := urlfetch.Client(c)

					// send the request to the ai
					req, err := http.NewRequest("POST", foundAi.URL, sendBody)
					var resp *http.Response
					if err == nil {
						req.Header.Set("Content-Type", "application/json; charset=UTF-8")
						resp, err = client.Do(req)
					}

					recvBody := &bytes.Buffer{}
					recvBodyString := ""
					if err == nil {
						// check what we received
						_, err = io.Copy(recvBody, resp.Body)
						recvBodyString = recvBody.String()
					}
					// if we have no other errors, but we got a non-200
					if err == nil && resp.StatusCode != 200 {
						err = orderError{
							Request:      req,
							RequestBody:  sendBodyString,
							Response:     resp,
							ResponseBody: recvBodyString,
						}
					}

					// lets try to unserialize
					if err == nil {
						err = json.Unmarshal(recvBody.Bytes(), &orderResp.Orders)
					}

					// store the error, if any
					if err != nil {
						orderResp.Error = err
					}
				}()
			} else {
				responses <- orderResp
			}
		}
		orderMap := map[state.PlayerId]state.Orders{}
		for _, _ = range self.Players {
			// wait for the responses
			orderResp := <-responses
			// store it
			orderMap[orderResp.StatePlayerId] = orderResp.Orders
			// if we got an error
			if orderResp.Error != nil {
				// make sure to save it later
				errorSavers = append(errorSavers, func() {
					if ai := GetAIById(con, orderResp.DatastorePlayerId); ai != nil {
						ai.AddError(con, lastTurn.Id, orderResp.Error)
					}
				})
			}
		}
		// execute the orders
		newTurn, winner := lastTurn.Next(c, orderMap)
		// save the new turn
		newTurn.Save(c, self.Id)
		// if we got a winner, end the game and store the winner
		if winner == nil {
			self.State = StatePlaying
		} else {
			self.Winner = common.MustDecodeKey(string(*winner))
			self.State = StateFinished
		}
		// increase our length with the new turn
		self.Length += 1
		// save us
		self.Save(c)
		// if we didn't end, queue the next turn
		if winner == nil {
			nextTurnFunc.Call(c, self.Id, playerNames)
		}
		return nil
	}); err != nil {
		panic(err)
	}
	// run any error savers we got
	for _, saver := range errorSavers {
		saver()
	}
	// store the new stats in the players if we ended
	if self.State == StateFinished {
		for _, playerId := range self.Players {
			common.Transaction(con, func(c common.Context) error {
				if ai := GetAIById(c, playerId); ai != nil {
					if playerId.Equal(self.Winner) {
						ai.Wins += 1
					} else {
						ai.Losses += 1
					}
					ai.Save(c)
				}
				return nil
			})
		}
	}
}