def invite_player(gid, iid, invitee):
  """ Add invitee to the list of players invited to the specified instance.

  Args:
    gid: The game id of the Game object that this method targets.
    iid: The instance id of the Game Instance object that this
      method targets.
    invitee: The player id of the person to invite.

  Only modifies the instance if the player has not already been
  invited and has not joined the game.

  Returns:
    A tuple of the game instance and a single item dictionary:
      inv: The email address of the invited player if they are
        invited. If the player is not invited (because they have
        already been invited or have already joined the game), the
        value of 'inv' is the empty string.

  Raises:
    ValueError if the game id, iid or invitee email address are
    invalid.
  """
  utils.check_gameid(gid)
  utils.check_instanceid(iid)
  player = utils.check_playerid(invitee)
  instance = utils.get_instance_model(gid, iid)
  if player not in instance.invited and player not in instance.players:
    instance.invited.append(player)
    instance.put()
  else:
    player = ''
  return instance, {INVITEE_KEY : player}
def set_leader(gid, iid, pid, leader):
  """ Set the leader of the specified instance.

  Args:
    gid: The game id of the GameInstance object's parent Game object.
    iid: The instance id of the GameInstance to change the leader of.
    pid: The player id of the requesting player. This player must be
      the current instance leader in oder to change the leader value.
    leader: The player id of the new leader.

  Returns:
    A tuple of the change game instance model and a dictionary with
    two items:
      'current_leader' : The leader after attempting this change.
      'leader_change' : Whether or not this attempt to set the leader
        succeeded.
  Raises:
    ValueError if the game id or instance id are invalid.
    ValueError if player or leader are in the specified game
      instance.
  """
  utils.check_gameid(gid)
  utils.check_instanceid(iid)
  instance = utils.get_instance_model(gid, iid)
  player = instance.check_player(pid)
  leader = instance.check_player(leader)
  if player != instance.leader or instance.leader == leader:
    return instance, {'current_leader' : instance.leader,
                      'leader_changed' : False}
  instance.leader = leader
  instance.put()
  return instance, {'current_leader' : leader,
                    'leader_changed' : True}
def server_command(gid, iid, pid, command, arguments):
  """ Performs the desired server command.

  Args:
    gid: The game id of the Game model for this operation.
    iid: The instance id of the GameInstance model for
      this operation.
    pid: The player id of the requesting player.
    command: The key identifying the command to execute.
    arguments: JSON representation of arguments to the command.

  If the gid and iid specify a valid game instance model it will be
  passed to the server command. In the case that the iid is empty or
  refers to a game instance that doesn't exist, a game model will be
  used. Most commands will fail if passed a game model instead of a
  game instance, but some are indifferent to the model passed to
  them.

  Unless the dynamic property do_not_put has been set to False, this
  will put the database model after the command has been
  performed. This means that server commands do not need to make
  intermediate puts of the instance model passed to them.

  Returns:
    A tuple of the model used in the server command's execution and a
    two item dictionary:
      'type': The requested command key.
      'contents': A Python value of the response value of the
        command. This varies among server commands but must always be
        able to be encoded to JSON.

  Raises:
    ValueError if the game id or player id is invalid.
    ValueError if the arguments json cannot be parsed.
    ValueError if command is not a known server command.
  """
  utils.check_gameid(gid)
  player = utils.check_playerid(pid)
  model = None
  if iid:
    model = utils.get_instance_model(gid, iid)
  if model is None:
    model = utils.get_game_model(gid)
    if model is None:
      model = Game(key_name = gid, instance_count = 0)

  arguments = simplejson.loads(arguments)
  reply = ''

  if command in command_dict:
    reply = command_dict[command](model, player, arguments)
    if 'do_not_put' not in model.dynamic_properties() or not model.do_not_put:
      model.put()
  else:
    raise ValueError("Invalid server command: %s." % command)

  if not isinstance(reply, list):
    reply = [reply]
  return model, {TYPE_KEY : command, CONTENTS_KEY: reply}
def new_message(gid, iid, pid, message_type, message_recipients,
                message_content):
  """ Create new messages and put them in the database.

  Args:
    gid: The game id of the Game parent of the instance to create a
      message for.
    iid: The instance id of the GameInstance to create a message for.
    pid: The player id of the message sender.
    message_type: A string that acts as a key for the message.
    message_recipients: The recipients of the message formatted in
      JSON. This can be a single player id as a JSON string, a list
      of player ids in a JSON array or the empty string. Messages
      sent with the empty string as a recipient can be fetched by
      any player.
    message_content: The string representation of a JSON value to be
      sent as the content of the message.

  Returns:
    A tuple of the specified game instance and a dictionary with
    two items:
      'count' : The number of messages created.
      'mrec' : The list of email addresses that were sent messages.

  Raises:
    ValueError if the requesting player or any of the message
      recipients are not members of the specified game instance.
  """
  utils.check_gameid(gid)
  utils.check_instanceid(iid)
  instance = utils.get_instance_model(gid, iid)
  player = instance.check_player(pid)
  recipients_list = None
  if message_recipients != '':
    recipients_list = simplejson.loads(message_recipients)
    if isinstance(recipients_list, basestring):
      recipients_list = [recipients_list]
  if not recipients_list:
    recipients_list = ['']
  message_list = []
  for recipient_entry in recipients_list:
    if recipient_entry:
      recipient_entry = instance.check_player(recipient_entry)
    message = Message(parent = instance,
                      sender = player,
                      msg_type = message_type,
                      recipient = recipient_entry,
                      content = message_content)
    message_list.append(message)
  db.put(message_list)
  return instance, {MESSAGE_COUNT_KEY : len(message_list),
                    MESSAGE_RECIPIENTS_KEY : recipients_list}
def leave_instance(gid, iid, pid):
  """ Remove a player from an instance.

  Args:
    gid: The game id of the game object that this method targets.
    iid: The instance id of the Game Instance to remove the player
      from.
    player: The player wishing to leave the instance.

  If the player that leaves the instance is the leader, the first
  player on the players lists becomes the leader.

  If no players are left, the maximum number of players allowed in
  this instance is set to -1 so that no one may join it in the
  future. This means that if someone tries to create an instance in
  the future with the same instance id, they will end up with one with
  a number appended to it (because this GameInstance object will still
  exist).

  The decision to do this was made because it is not yet possible to
  reliably delete all of the messages in a game instance (see
  models/game_instance.py). Thus, if players are able to join an
  orphaned instances, the old messages could still be available. If,
  in the future, App Engine adds ways to reliably delete database
  models this behavior could be changed to delete the instance
  entirely if everyone leaves.

  Returns:
    A tuple of the game object and the instance list dictionary
    for this player (see get_instance_lists_as_dictionary).

  Raises:
    ValueError if the player is not currently in the instance.
  """
  utils.check_gameid(gid)
  utils.check_instanceid(iid)
  instance = utils.get_instance_model(gid, iid)
  player = instance.check_player(pid)
  instance.players.remove(player)
  if player == instance.leader and len(instance.players) != 0:
    instance.leader = instance.players[0]
  if len(instance.players) == 0:
    instance.max_players = -1
  game = instance.parent()
  instance_lists = get_instances_lists_as_dictionary(game, player)
  instance_lists['joined'].remove(instance.key().name())
  instance.put()
  return game, instance_lists
def join_instance(gid, iid, pid):
  """ Attempt to add a player to an instance.

  Args:
    gid: The game id of the Game object that this method targets.
    iid: The instance id of the Game Instance to join.
    pid: A string containing the requesting player's email address.

  A player can join a game instance if it is not full and either the
  instance is public or the player has been invited. If this
  operation is invoked by a player that is not current in the
  specified instance and they are unable to join, it will fail.

  If the player is already in the game instance this will succeed
  without modifying the instance.

  If the specified game instance doesn't exist, it will be created as
  in new_instance with the specified instance id.

  If no players are in the game when this player tries to join they
  will automatically become the leader.

  Returns:
    A tuple of the game instance and the instance list dictionary
    for this player (see get_instance_lists_as_dictionary).

  Raises:
    ValueError if the game id, instance id or player id are invalid.
    ValueError if the player is not already in the game and is unable
      to join.
  """
  utils.check_gameid(gid)
  utils.check_instanceid(iid)
  player = utils.check_playerid(pid)
  instance = utils.get_instance_model(gid, iid)
  if instance is None:
    return new_instance(gid, iid, pid)
  game = instance.parent()
  instance_lists = get_instances_lists_as_dictionary(game, player)
  instance.add_player(player)
  instance.put()
  if iid in instance_lists['invited']:
    instance_lists['invited'].remove(instance.key().name())
  if iid not in instance_lists['joined']:
    instance_lists['joined'].append(instance.key().name())
  return instance, instance_lists
def new_instance(gid, iid_prefix, pid, make_public = False):
  """ Create a new instance of the specified game.

  Args:
    gid: The game id of the Game parent of the new instance.
    iid_prefix: The desired instance id. If no instance has been made
      with this name before, then this will be the instance id of the
      newly created instance. However, since instance ids must be
      unique, the actual instance id will likely be iid_prefix with a
      number suffix.
    pid: The id of the first player and leader of the game.
    make_public: A boolean indicating whether this instance should
      be able to be seen and joined by anyone.

  The instance id will start with iid_prefix, but could have any
  suffix. If the parent Game object does not exist, it will
  automatically be created.

  Returns:
    A tuple of the newly created instance and an instance lists
    dictionary (see get_instance_lists_as_dictionary).

  Raises:
    ValueError if the gameid or player id are invalid.
  """
  utils.check_gameid(gid)
  player = utils.check_playerid(pid)
  game = Game.get_by_key_name(gid)
  if game is None:
    game = Game(key_name = gid, instance_count = 0)

  if not iid_prefix:
    iid_prefix = player + 'instance'
  instance = game.get_new_instance(iid_prefix, player)

  instance_lists = get_instances_lists_as_dictionary(game, player)
  instance_lists['joined'].append(instance.key().name())
  if make_public:
    instance.public = True
    instance_lists['public'].append(instance.key().name())
  instance.put()
  game.put()

  return instance, instance_lists
def get_instance(gid, iid):
  """ Retrieves an instance and its dictionary.

  Args:
    gid: The game id of the desired GameInstance object's parent Game
      object.
    iid: The instance id of the desired GameInstance object.

  Returns:
    A tuple of the game instance object and its dictionary
    representation.

  Raises:
    ValueError if the game id or instance id are not valid.
  """
  utils.check_gameid(gid)
  utils.check_instanceid(iid)
  instance = utils.get_instance_model(gid, iid)
  return instance, instance.to_dictionary()
def get_messages(gid, iid, message_type, recipient, count, time):
  """ Retrieve messages matching the specified parameters.

  Args:
    gid: The game id of the Game object that is a parent of the
      desired instance.
    iid: This instance id of the Game Instance to fetch messages
      from.
    message_type: A string 'key' for the message. If message_type is
      the empty string, all message types will be returned.
    recipient: The player id of the recipient of the messages. This
      operation will also return messages that are sent with an empty
      recipient field.
    count: The maximum number of messages to retrieve.
    time: A string representation of the earliest creation time of a
      message to returned. Must be in ISO 8601 format to parse
      correctly.

  Uses the get_messages function of the GameInstance class to
  retrieve messages.

  Returns:
    A tuple of the game instance and a dictionary with two items:
      'count': The number of messages returned.
      'messages': A list of the dictionary representations of the
        fetched messages.
  """
  utils.check_gameid(gid)
  utils.check_instanceid(iid)
  instance = utils.get_instance_model(gid, iid)
  recipient = instance.check_player(recipient)
  messages = instance.get_messages(count=count,
                                   message_type=message_type,
                                   recipient=recipient, time=time)
  return instance, {MESSAGE_COUNT_KEY : len(messages),
                    'messages' : messages}
def get_instance_lists(gid, iid, pid):
  """ Return the instances that a player has been invited to and joined.

  Args:
    gid: The game id of the Game object that this method targets.
    iid: The instance id of the Game Instance object that this
      method targets.
    pid: A string containing the requesting player's email address.

  The gid and pid must be valid, but the iid can be blank. This is
  because a player must be able to query for lists of instances
  without being in one.

  Returns:
    A tuple containing a database model and a dictionary of instance
    lists.  The database model will be a Game Instance if the gid and
    iid parameters specify a valid GameInstance, otherwise the model
    will be a Game.  Instance lists are returned in the same format
    as get_instance_lists_dictionary.

  Raises:
    ValueError if the game id or player id are invalid.
  """
  utils.check_gameid(gid)
  player = utils.check_playerid(pid)
  model = game = utils.get_game_model(gid)
  if game is None:
    game = Game(key_name = gid, instance_count = 0)
    game.put()
    model = game
  elif iid:
    instance = utils.get_instance_model(gid,iid)
    if instance:
      model = instance
  instance_lists = get_instances_lists_as_dictionary(game, player)
  return model, instance_lists