Skip to content

Commit

Permalink
Merge 3ffc83f into d61c3f5
Browse files Browse the repository at this point in the history
  • Loading branch information
OlafSzmidt committed Aug 31, 2018
2 parents d61c3f5 + 3ffc83f commit 1277138
Show file tree
Hide file tree
Showing 40 changed files with 626 additions and 626 deletions.
1 change: 0 additions & 1 deletion aimmo-game-worker/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def get_code_and_options():
def process_turn():
code, options = get_code_and_options()
data = flask.request.get_json()

world_map = WorldMap(**data['world_map'])

avatar_state = AvatarState(location=data['avatar_state']['location'],
Expand Down
76 changes: 29 additions & 47 deletions aimmo-game/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@
from flask_cors import CORS

from simulation import map_generator
from simulation.turn_manager import ConcurrentTurnManager
from simulation.logs import Logs
from simulation.avatar.avatar_manager import AvatarManager
from simulation.worker_managers import WORKER_MANAGERS
from simulation.communicator import Communicator
from simulation.game_runner import GameRunner

eventlet.sleep()
Expand All @@ -31,14 +27,11 @@


class GameAPI(object):
def __init__(self, worker_manager, game_state, logs, have_avatars_code_updated):
def __init__(self, game_state, worker_manager):
self._socket_session_id_to_player_id = {}
self.register_endpoints()
self.worker_manager = worker_manager
self.logs = logs
self.game_state = game_state
self._sid_to_avatar_id = {}
self.have_avatars_code_updated = have_avatars_code_updated

self.register_endpoints()

def register_endpoints(self):
self.register_player_data_view()
Expand Down Expand Up @@ -66,8 +59,6 @@ def player_data(player_id):
def register_world_update_on_connect(self):
@socketio_server.on('connect')
def world_update_on_connect(sid, environ):
self._sid_to_avatar_id[sid] = None

query = environ['QUERY_STRING']
self._find_avatar_id_from_query(sid, query)
self.send_updates()
Expand All @@ -79,16 +70,17 @@ def register_remove_session_id_from_mappings(self):
def remove_session_id_from_mappings(sid):
LOGGER.info("Socket disconnected for session id:{}. ".format(sid))
try:
del self._sid_to_avatar_id[sid]
del self._socket_session_id_to_player_id[sid]
except KeyError:
pass

return remove_session_id_from_mappings

def send_updates(self):
self._send_game_state()
self._send_logs()
self._send_have_avatars_code_updated()
player_id_to_worker = self.worker_manager.player_id_to_worker
self._send_logs(player_id_to_worker)
self._send_have_avatars_code_updated(player_id_to_worker)

def _find_avatar_id_from_query(self, session_id, query_string):
"""
Expand All @@ -100,62 +92,52 @@ def _find_avatar_id_from_query(self, session_id, query_string):

try:
avatar_id = int(parsed_qs['avatar_id'][0])
self._sid_to_avatar_id[session_id] = avatar_id
self._socket_session_id_to_player_id[session_id] = avatar_id
except ValueError:
LOGGER.error("Avatar ID could not be casted into an integer")
except KeyError:
LOGGER.error("No avatar ID found. User may not be authorised ")
LOGGER.error("query_string: " + query_string)

def _send_logs(self):
def _send_logs(self, player_id_to_workers):
def should_send_logs(logs):
LOGGER.info("should_send_logs: " + str(logs))

return logs is not None and logs != ''

for sid, avatar_id in self._sid_to_avatar_id.iteritems():
avatar_logs = self.logs.get_user_logs(avatar_id)
for sid, player_id in self._socket_session_id_to_player_id.iteritems():
avatar_logs = player_id_to_workers[player_id].log
if should_send_logs(avatar_logs):
socketio_server.emit('log', avatar_logs, room=sid)

def _send_game_state(self):
serialised_game_state = self.game_state.serialise()
for sid, avatar_id in self._sid_to_avatar_id.iteritems():
for sid, player_id in self._socket_session_id_to_player_id.iteritems():
socketio_server.emit('game-state', serialised_game_state, room=sid)

def _send_have_avatars_code_updated(self):
for sid, avatar_id in self._sid_to_avatar_id.iteritems():
if self.have_avatars_code_updated.get(avatar_id, False):
def _send_have_avatars_code_updated(self, player_id_to_workers):
for sid, player_id in self._socket_session_id_to_player_id.iteritems():
if player_id_to_workers[player_id].has_code_updated:
socketio_server.emit('feedback-avatar-updated', room=sid)


def run_game(port):
print("Running game...")
def create_runner(port):
settings = pickle.loads(os.environ['settings'])
api_url = os.environ.get('GAME_API_URL', 'http://localhost:8000/aimmo/api/games/')
generator = getattr(map_generator, settings['GENERATOR'])(settings)
player_manager = AvatarManager()
worker_manager_class = WORKER_MANAGERS[os.environ.get('WORKER_MANAGER', 'local')]

communicator = Communicator(api_url=api_url, completion_url=api_url + 'complete/')
game_state = generator.get_game_state(player_manager)
return GameRunner(worker_manager_class=worker_manager_class,
game_state_generator=generator.get_game_state,
django_api_url=os.environ.get('GAME_API_URL', 'http://localhost:8000/aimmo/api/games/'),
port=port)

WorkerManagerClass = WORKER_MANAGERS[os.environ.get('WORKER_MANAGER', 'local')]
worker_manager = WorkerManagerClass(port=port)

logs = Logs()
have_avatars_code_updated = {}

game_api = GameAPI(worker_manager, game_state, logs, have_avatars_code_updated)

turn_manager = ConcurrentTurnManager(end_turn_callback=game_api.send_updates,
communicator=communicator,
game_state=game_state,
logs=logs,
have_avatars_code_updated=have_avatars_code_updated)

game_runner = GameRunner(worker_manager=worker_manager,
game_state=game_state,
communicator=communicator)

def run_game(port):
game_runner = create_runner(port)
game_api = GameAPI(game_state=game_runner.game_state,
worker_manager=game_runner.worker_manager)
game_runner.set_end_turn_callback(game_api.send_updates)
game_runner.start()
turn_manager.start()


if __name__ == '__main__':
Expand Down
5 changes: 3 additions & 2 deletions aimmo-game/simulation/avatar/avatar_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ class AvatarManager(object):
def __init__(self):
self.avatars_by_id = {}

def add_avatar(self, player_id, worker_url, location):
avatar = AvatarWrapper(player_id, location, worker_url,
def add_avatar(self, player_id, location):
avatar = AvatarWrapper(player_id, location,
AvatarAppearance("#000", "#ddd", "#777", "#fff"))
self.avatars_by_id[player_id] = avatar
return avatar
Expand All @@ -36,3 +36,4 @@ def serialise_players(self):
"""

return [player.serialise() for player in self.avatars]

20 changes: 3 additions & 17 deletions aimmo-game/simulation/avatar/avatar_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class AvatarWrapper(object):
the player-supplied code.
"""

def __init__(self, player_id, initial_location, worker_url, avatar_appearance):
def __init__(self, player_id, initial_location, avatar_appearance):
self.player_id = player_id
self.location = initial_location
self.previous_location = initial_location
Expand All @@ -22,7 +22,6 @@ def __init__(self, player_id, initial_location, worker_url, avatar_appearance):
self.score = 0
self.events = []
self.avatar_appearance = avatar_appearance
self.worker_url = worker_url
self.effects = set()
self.resistance = 0
self.attack_strength = 1
Expand All @@ -46,19 +45,6 @@ def action(self):
def is_moving(self):
return isinstance(self.action, MoveAction)

def fetch_data(self, state_view):
try:
response = requests.post(self.worker_url, json=state_view)
response.raise_for_status()
return response.json()
except requests.exceptions.ConnectionError:
LOGGER.info('Could not connect to worker, probably not ready yet')
except Exception as e:
LOGGER.exception("Unknown error while fetching turn data.")
LOGGER.exception(e)

return {'action': None, 'log': '', 'avatar_updated': False}

def _construct_action(self, action_data):
action_type = action_data['action_type']
action_args = action_data.get('options', {})
Expand All @@ -82,9 +68,9 @@ def calculate_orientation(self):

return direction_of_orientation.cardinal

def decide_action(self, worker_data):
def decide_action(self, serialised_action):
try:
action = self._construct_action(worker_data['action'])
action = self._construct_action(serialised_action)

except (KeyError, ValueError) as err:
LOGGER.error('Bad action data supplied: %s', err)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import requests


class Communicator(object):
class DjangoCommunicator(object):
"""
This class encapsulates the communication between aimmo-game
and the django server
"""
def __init__(self, api_url, completion_url):
self.api_url = api_url
def __init__(self, django_api_url, completion_url):
self.django_api_url = django_api_url
self.completion_url = completion_url

def get_game_metadata(self):
return requests.get(self.api_url).json()
return requests.get(self.django_api_url).json()

def mark_game_complete(self, data=None):
return requests.post(requests.post(self.completion_url, json=data))
44 changes: 33 additions & 11 deletions aimmo-game/simulation/game_runner.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,69 @@
import time
import threading

WORKER_UPDATE_SLEEP_TIME = 10
from django_communicator import DjangoCommunicator
from simulation_runner import ConcurrentSimulationRunner
from avatar.avatar_manager import AvatarManager

TURN_TIME = 2


class GameRunner(threading.Thread):
def __init__(self, worker_manager, game_state, communicator):
def __init__(self, worker_manager_class, game_state_generator, django_api_url, port):
super(GameRunner, self).__init__()
self.worker_manager = worker_manager
self.game_state = game_state
self.communicator = communicator
self.worker_manager = worker_manager_class(port=port)
self.game_state = game_state_generator(AvatarManager())
self.communicator = DjangoCommunicator(django_api_url=django_api_url,
completion_url=django_api_url + 'complete/')
self.subscriber = None
self.simulation_runner = ConcurrentSimulationRunner(communicator=self.communicator,
game_state=self.game_state)
self._end_turn_callback = lambda: None

def set_end_turn_callback(self, callback_method):
self._end_turn_callback = callback_method

def get_users_to_add(self, game_metadata):
def player_is_new(_player):
return _player['id'] not in self.worker_manager.avatar_id_to_worker.keys()
return _player['id'] not in self.worker_manager.player_id_to_worker.keys()

return [player['id'] for player in game_metadata['users'] if player_is_new(player)]

def get_users_to_delete(self, game_metadata):
def player_in_worker_manager_but_not_metadata(pid):
return pid not in [player['id'] for player in game_metadata['users']]

return [player_id for player_id in self.worker_manager.avatar_id_to_worker.keys()
return [player_id for player_id in self.worker_manager.player_id_to_worker.keys()
if player_in_worker_manager_but_not_metadata(player_id)]

def update_main_user(self, game_metadata):
self.game_state.main_avatar_id = game_metadata['main_avatar']

def update(self):
def update_workers(self):
game_metadata = self.communicator.get_game_metadata()['main']

users_to_add = self.get_users_to_add(game_metadata)
users_to_delete = self.get_users_to_delete(game_metadata)

worker_urls = self.worker_manager.add_workers(users_to_add)
self.worker_manager.add_workers(users_to_add)
self.worker_manager.delete_workers(users_to_delete)
self.game_state.add_avatars(users_to_add, worker_urls)
self.game_state.add_avatars(users_to_add)
self.game_state.delete_avatars(users_to_delete)
self.worker_manager.update_worker_codes(game_metadata['users'])

self.update_main_user(game_metadata)
self.worker_manager.fetch_all_worker_data(self.game_state.get_serialised_game_states_for_workers())

def update_simulation(self, player_id_to_serialised_actions):
self.simulation_runner.run_single_turn(player_id_to_serialised_actions)
self._end_turn_callback()

def update(self):
self.update_workers()
self.update_simulation(self.worker_manager.get_player_id_to_serialised_actions())
self.worker_manager.clear_logs()

def run(self):
while True:
self.update()
time.sleep(WORKER_UPDATE_SLEEP_TIME)
time.sleep(TURN_TIME)
31 changes: 18 additions & 13 deletions aimmo-game/simulation/game_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,15 @@ def __init__(self, world_map, avatar_manager, completion_check_callback=lambda:
self.main_avatar_id = None
self._lock = RLock()

def get_state_for(self, avatar_wrapper):
with self._lock:
return {
'avatar_state': avatar_wrapper.serialise(),
'world_map': {
'cells': [cell.serialise() for cell in self.world_map.all_cells()]
}
}

def add_avatar(self, player_id, worker_url, location=None):
def add_avatar(self, player_id, location=None):
with self._lock:
location = self.world_map.get_random_spawn_location() if location is None else location
avatar = self.avatar_manager.add_avatar(player_id, worker_url, location)
avatar = self.avatar_manager.add_avatar(player_id, location)
self.world_map.get_cell(location).avatar = avatar

def add_avatars(self, player_ids, worker_url_bases):
def add_avatars(self, player_ids):
for player_id in player_ids:
self.add_avatar(player_id, '{}/turn/'.format(worker_url_bases[player_id]))
self.add_avatar(player_id)

def delete_avatars(self, player_ids):
for player_id in player_ids:
Expand Down Expand Up @@ -75,3 +66,17 @@ def serialise(self):
'scoreLocations': (self.world_map.serialise_score_location()),
'obstacles': self.world_map.serialise_obstacles()
}

def serialise_for_worker(self, avatar_wrapper):
with self._lock:
return {
'avatar_state': avatar_wrapper.serialise(),
'world_map': {
'cells': [cell.serialise() for cell in self.world_map.all_cells()]
}
}

def get_serialised_game_states_for_workers(self):
with self._lock:
return {player_id: self.serialise_for_worker(avatar_wrapper) for player_id, avatar_wrapper
in self.avatar_manager.avatars_by_id.iteritems()}
20 changes: 0 additions & 20 deletions aimmo-game/simulation/logs.py

This file was deleted.

Loading

0 comments on commit 1277138

Please sign in to comment.