text: import json
  import datetime
  import time
  import os
  import boto3
  from boto3.dynamodb.conditions import Key, Attr
  import redis
  from datetime import timedelta
  import random
  from redis.exceptions import (
      ConnectionError,
      DataError,
      ExecAbortError,
      NoScriptError,
      PubSubError,
      RedisError,
      ResponseError,
      TimeoutError,
      WatchError,
  )
  
  # Tries to find an existing or free game session and return the IP and Port to the client
  
  def lambda_handler(event, context):
  
      # Get redis endpoint and set up
      redis_endpoint = os.environ['REDIS_ENDPOINT']
      # Setup Redis client
      redis_client = redis.Redis(host=redis_endpoint, port=6379, db=0)
  
      # 1. Check if there are game servers that have players in them but are not full yet    
      print("Checking if there are active servers with players already")
      active_game_servers_response = redis_client.scan(count=100000,match="active-gameserver-*")
      if len(active_game_servers_response[1]) > 0:
          print("Found an active game server, trying to take the spot")
  
          # Try to claim a spot on a random active game server
          # Use WATCH locking to cancel the placement in case someone else took it a the same time
          with redis_client.pipeline() as pipe:
              # We'll try this 25 times and then just fail
              for x in range(25):
                  try:
                      # Take a random active game server
                      game_server_key = random.choice(active_game_servers_response[1])
  
                      # put a WATCH on a lock for this specific key
                      pipe.watch(b'-lock'+game_server_key)
  
                      # get the current reservation and max players for this game server.
                      # Server will use "current-players" for actually connected players
                      current_reservations = pipe.hget(game_server_key, b'reserved-player-slots')
                      max_players = pipe.hget(game_server_key, b'max-players')
                      print("current reservations: " + str(current_reservations))
                      print("max players: " + str(max_players))
  
                      if current_reservations == None:
                          current_reservations = 0
  
                      # Check if this server is full already
                      if int(current_reservations) >= int(max_players):
                          print("Server full, cannot join")
                          continue
  
                      next_value = int(current_reservations) + 1
  
                      # now we can put the pipeline back into buffered mode with MULTI and try to update
                      pipe.multi()
                      pipe.hset(game_server_key, b'reserved-player-slots', next_value)
                      pipe.hset(game_server_key, b'last-reservation-time', time.time())
                      # Update lock
                      pipe.set(b'-lock'+game_server_key, "")
                      pipe.expire(b'-lock'+game_server_key, timedelta(seconds=3))
                      # and finally, execute the pipeline (the set command)
                      pipe.execute()
  
                      # If we reached here, there was no WatchError
                      print("Successfully taken the spot, return IP and port to client")
  
                      publicIP = redis_client.hget(game_server_key, b'publicIP')
                      port = redis_client.hget(game_server_key, b'port')
  
                      print("Got server: " + str(publicIP) + ":" + str(port))
  
                      return {
                          "statusCode": 200,
                          "body": json.dumps({ 'publicIP': publicIP.decode('UTF-8'), 'port': port.decode('UTF-8') })
                      }
                  except WatchError:
                      # another client must have changed the game server data in between
                      # the time we started WATCHing it and the pipeline's execution.
                      # We will retry a placement
                      print("Failed to reserve slot, retrying")
  
  
      # 2. As no game servers with players in them found, search for a free available game server
      # We'll try this 30 times and then just fail
      for x in range(30):
          try:
              #   Check priority list first for the first 20 rounds (Game servers on Tasks that already hosted sessions) for good rotation of Tasks
              #   For the last 10 rounds we switch to non-priority to make sure any issues in priority won't fail us completely
              available_game_servers_response = None
              if x < 20:
                  print("No active game sessions, checking priority servers first from available")
                  available_game_servers_response = redis_client.scan(count=100000,match="available-priority-gameserver-*")
  
              if available_game_servers_response == None or len(available_game_servers_response[1]) == 0:
                  # No priority servers, check list of servers on fresh Tasks
                  print("No priority servers. Checking if there are available servers with no players")
                  available_game_servers_response = redis_client.scan(count=100000,match="available-gameserver-*")
  
              if len(available_game_servers_response[1]) > 0:
                  print("Found an available game server, trying to take the spot")
                  # You can use these 3 lines for debugging if you need more information on the server
                  #print(available_game_servers_response[1])
                  #server_info = redis_client.hgetall(available_game_servers_response[1][0])
                  #print(server_info)
  
              # Try to claim a spot on a random available game server
              # Use WATCH locking to cancel the placement in case someone else took it a the same time
              with redis_client.pipeline() as pipe:
  
                  # Take a random available game server
                  game_server_key = random.choice(available_game_servers_response[1])
  
                  # Make sure the server is ready to accept connections
                  server_ready = redis_client.hget(game_server_key, b'ready')
                  print("Server ready: " + str(server_ready))
                  if int(server_ready) == 0:
                      print("Server not ready yet, retry.")
                      continue
  
                  # put a WATCH on a lock for this specific key
                  pipe.watch(b'-lock'+game_server_key)
  
                  # get the current reservation and max players for this game server.
                  # Server will use "current-players" for actually connected players
                  current_reservations = pipe.hget(game_server_key, b'reserved-player-slots')
                  max_players = pipe.hget(game_server_key, b'max-players')
                  print("current reservations: " + str(current_reservations))
                  print("max players: " + str(max_players))
  
                  if current_reservations == None:
                      current_reservations = 0
  
                  # Check if this was preserved full already
                  if int(current_reservations) >= int(max_players):
                      print("Server full, cannot join")
                      continue
  
                  next_value = int(current_reservations) + 1
  
                  # now we can put the pipeline back into buffered mode with MULTI and try to update
                  pipe.multi()
                  pipe.hset(game_server_key, b'reserved-player-slots', next_value)
                  pipe.hset(game_server_key, b'last-reservation-time', time.time())
                  # Update lock
                  pipe.set(b'-lock'+game_server_key, "")
                  pipe.expire(b'-lock'+game_server_key, timedelta(seconds=3))
                  # and finally, execute the pipeline (the set command)
                  pipe.execute()
  
                  # If we reached here, there was no WatchError
                  print("Successfully taken the spot, return IP and port to client")
  
                  publicIP = redis_client.hget(game_server_key, b'publicIP')
                  port = redis_client.hget(game_server_key, b'port')
  
                  print("Got server: " + str(publicIP) + ":" + str(port))
  
                  return {
                      "statusCode": 200,
                      "body": json.dumps({ 'publicIP': publicIP.decode('UTF-8'), 'port': port.decode('UTF-8') })
                  }
          except WatchError:
              # another client must have changed 'OUR-SEQUENCE-KEY' between
              # the time we started WATCHing it and the pipeline's execution.
              # our best bet is to just retry.
              print("Failed to reserve slot, retrying")
  
      # Failed to find a server
      return {
              "statusCode": 500,
              "body": json.dumps({ 'failed': 'couldnt find a free server spot'})
      }