in BackendServices/functions/requestgamesession.py [0:0]
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'})
}