Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix kubernetes mode #906

Merged
merged 32 commits into from
Nov 29, 2018
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
24bc1bf
fixed default port not being given in dockerfile for games
Nov 21, 2018
c601674
Changed simulation runner to make use of asyncio
Nov 21, 2018
6b2bf35
changed parallel_map to async_map
Nov 22, 2018
d973a58
Waiting for workers pt. 1
faucomte97 Nov 22, 2018
8053284
aimmo now works in kubernetes mode again
Nov 22, 2018
8853705
aimmo now works in kubernetes mode again (tests fails though)
Nov 22, 2018
8e72e99
Merge remote-tracking branch 'origin/fix_kubernetes_mode' into fix_ku…
Nov 22, 2018
b8f6f7e
fixed broken tests
Nov 23, 2018
2bc24dc
pep8
Nov 23, 2018
fb00179
removed temporary logging
Nov 23, 2018
1920f18
moved get loop into setUp for tests
Nov 23, 2018
0e057be
bump version++
Nov 26, 2018
287912f
make runner last build stage
Nov 26, 2018
f8715f5
remove broken coverage tool
Nov 26, 2018
55ebb1c
removed broken coverage tool++
Nov 26, 2018
3ab42a5
change docker build target in tester
Nov 26, 2018
4a4e62f
docker now uses buildkit (requires docker 18.09)
Nov 27, 2018
812dc25
create build hook for docker cloud
Nov 27, 2018
5aedac4
fix syntax error
Nov 27, 2018
83d8b5c
add image tag to build hook
Nov 27, 2018
505c63a
fix image name on build hook
Nov 27, 2018
ab954e5
add build hook for worker and game-creator
Nov 27, 2018
2f274f6
remove pwd from build hooks
Nov 27, 2018
024d431
remove broken coverage tool from travis.yml
Nov 27, 2018
52aefca
change env variable to be str not int
Nov 27, 2018
306bee8
force setup.py version
Nov 28, 2018
db2ab81
Revert "force setup.py version"
Nov 28, 2018
f78f1f7
review changes
Nov 29, 2018
d8edab5
add dockerbuildkit env variable to travis
Nov 29, 2018
6ed36c9
change the way docker is installed for travis test
Nov 29, 2018
460f09a
removed docker addon from travis.yml (installing older version)
Nov 29, 2018
086d8c2
changed logic in docker scripts
Nov 29, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aimmo-game/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ COPY . .
FROM base as runner
RUN apk add --no-cache bash
ENV WORKER_MANAGER=kubernetes
CMD python ./service.py 0.0.0.0
CMD python ./service.py 0.0.0.0 5000

FROM base as tester
ENV WORKER_MANAGER=kubernetes
Expand Down
3 changes: 1 addition & 2 deletions aimmo-game/simulation/game_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ def update_workers(self):
game_metadata = self.communicator.get_game_metadata()['main']

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

self.worker_manager.add_workers(users_to_add)
Expand All @@ -61,7 +60,7 @@ def update_workers(self):
self.worker_manager.fetch_all_worker_data(self.game_state.get_serialised_game_states_for_workers())

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

async def update(self):
Expand Down
35 changes: 15 additions & 20 deletions aimmo-game/simulation/simulation_runner.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import logging
import asyncio
from abc import ABCMeta, abstractmethod
from concurrent import futures
from concurrent.futures import ALL_COMPLETED
from simulation.action import PRIORITIES
from concurrent.futures import ALL_COMPLETED, ThreadPoolExecutor
from simulation.action import PRIORITIES, WaitAction
from threading import Thread

LOGGER = logging.getLogger(__name__)
Expand All @@ -22,10 +22,10 @@ def __init__(self, game_state, communicator):
self.communicator = communicator

@abstractmethod
def run_turn(self, player_id_to_serialised_actions):
async def run_turn(self, player_id_to_serialised_actions):
pass

def _run_turn_for_avatar(self, avatar, serialised_action):
async def _run_turn_for_avatar(self, avatar, serialised_action):
"""
Send an avatar its view of the game state and register its
chosen action & logs.
Expand All @@ -50,44 +50,39 @@ def _update_environment(self, game_state):
def _mark_complete(self):
self.communicator.mark_game_complete(data=self.game_state.serialise())

def run_single_turn(self, player_id_to_serialised_actions):
self.run_turn(player_id_to_serialised_actions)
async def run_single_turn(self, player_id_to_serialised_actions):
await self.run_turn(player_id_to_serialised_actions)
self.game_state.update_environment()


class SequentialSimulationRunner(SimulationRunner):
def run_turn(self, player_id_to_serialised_actions):
async def run_turn(self, player_id_to_serialised_actions):
"""
Get and apply each avatar's action in turn.
"""
avatars = self.game_state.avatar_manager.active_avatars

for avatar in avatars:
self._run_turn_for_avatar(avatar, player_id_to_serialised_actions[avatar.player_id])
await self._run_turn_for_avatar(avatar, player_id_to_serialised_actions[avatar.player_id])
location_to_clear = avatar.action.target_location
avatar.action.process(self.game_state.world_map)
self.game_state.world_map.clear_cell_actions(location_to_clear)


class ConcurrentSimulationRunner(SimulationRunner):
def _parallel_map(self, func, iterable_args):
with futures.ThreadPoolExecutor() as executor:
results = executor.map(func, iterable_args)
futures.wait(results, timeout=2, return_when=ALL_COMPLETED)
return [results]
async def async_map(self, func, iterable_args):
futures = [func(*arg) for arg in iterable_args]
await asyncio.gather(*futures)

def run_turn(self, player_id_to_serialised_actions):
async def run_turn(self, player_id_to_serialised_actions):
"""
Concurrently get the intended actions from all avatars and register
them on the world map. Then apply actions in order of priority.
"""

avatars = self.game_state.avatar_manager.active_avatars
threads = [Thread(target=self._run_turn_for_avatar,
args=(avatar, player_id_to_serialised_actions[avatar.player_id])) for avatar in avatars]

[thread.start() for thread in threads]
[thread.join() for thread in threads]
args = [(avatar, player_id_to_serialised_actions[avatar.player_id]) for avatar in avatars]
await self.async_map(self._run_turn_for_avatar, args)

# Waits applied first, then attacks, then moves.
avatars.sort(key=lambda a: PRIORITIES[type(a.action)])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def _make_container(self, player_id):
env=[client.V1EnvVar(
name='DATA_URL',
value='%s/player/%d' % (self.game_url, player_id)),
kubernetes.client.V1EnvVar(
client.V1EnvVar(
name='PORT',
value='5000')],
name='aimmo-game-worker',
Expand Down
2 changes: 1 addition & 1 deletion aimmo-game/simulation/worker_managers/worker_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def update_code(self, player):

def add_new_worker(self, player_id):
worker_url_base = self.create_worker(player_id)
self.player_id_to_worker[player_id] = Worker('{}/turn/'.format(worker_url_base))
self.player_id_to_worker[player_id] = Worker(f'{worker_url_base}/turn/')

def _parallel_map(self, func, iterable_args):
with futures.ThreadPoolExecutor() as executor:
Expand Down
12 changes: 7 additions & 5 deletions aimmo-game/tests/functional/test_damage_pickups_and_effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from hypothesis import given, assume
from hypothesis import strategies as st
import math
import asyncio

from .mock_world import MockWorld
from simulation.location import Location
Expand All @@ -19,12 +20,13 @@ def setUp(self):
_avatar_spawn_cell = self.game.game_state.world_map.get_cell(Location(0, 0))
self.initial_attack_strength = _avatar_spawn_cell.avatar.attack_strength
self.cell = self.game.game_state.world_map.get_cell(Location(1, 0))
self.loop = asyncio.get_event_loop()

def test_damage_boost_pickup_can_be_picked_up_default(self):
pickup_created = DamageBoostPickup(self.cell)
self.cell.pickup = pickup_created

self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action())
self.loop.run_until_complete(self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action()))

self.assertEqual(self.cell.avatar, self.game.avatar_manager.get_avatar(1))
self.assertEqual(len(self.game.avatar_manager.get_avatar(1).effects), 1)
Expand All @@ -37,7 +39,7 @@ def test_damage_boost_pickup_can_be_picked_up_custom_integer(self, boost_value):
pickup_created = DamageBoostPickup(self.cell, boost_value)
self.cell.pickup = pickup_created

self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action())
self.loop.run_until_complete(self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action()))

self.assertEqual(self.cell.avatar, self.game.avatar_manager.get_avatar(1))
self.assertEqual(len(self.game.avatar_manager.get_avatar(1).effects), 1)
Expand All @@ -51,7 +53,7 @@ def test_damage_boost_pickup_can_be_picked_up_custom_floats(self, boost_value):
pickup_created = DamageBoostPickup(self.cell, boost_value)
self.cell.pickup = pickup_created

self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action())
self.loop.run_until_complete(self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action()))

self.assertEqual(self.cell.avatar, self.game.avatar_manager.get_avatar(1))
self.assertEqual(len(self.game.avatar_manager.get_avatar(1).effects), 1)
Expand All @@ -65,7 +67,7 @@ def test_damage_boost_increases_attack_strength_with_default_integer(self):
pickup_created = DamageBoostPickup(self.cell)
self.cell.pickup = pickup_created

self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action())
self.loop.run_until_complete(self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action()))

self.assertTrue(self.cell.avatar.attack_strength, self.initial_attack_strength + DAMAGE_BOOST_DEFAULT)

Expand All @@ -78,6 +80,6 @@ def test_damage_boost_increases_attack_strength_with_custom_integers(self, boost
pickup_created = DamageBoostPickup(self.cell, boost_value)
self.cell.pickup = pickup_created

self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action())
self.loop.run_until_complete(self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action()))

self.assertTrue(self.cell.avatar.attack_strength, self.initial_attack_strength + boost_value)
49 changes: 27 additions & 22 deletions aimmo-game/tests/functional/test_effect_expiry.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from unittest import TestCase
import asyncio

from .mock_world import MockWorld

Expand Down Expand Up @@ -39,20 +40,21 @@ def test_single_damage_boost_pickup_expiry(self):
self.assertEqual(self.avatar.attack_strength, 1)

# Avatar moves EAST to (1,0) where pickup is located, then repeats it 5 times.
loop = asyncio.get_event_loop()
for i in range(6):
self.game.simulation_runner.run_single_turn(
loop.run_until_complete(self.game.simulation_runner.run_single_turn(
self.game.avatar_manager.get_player_id_to_serialised_action()
)
))

self.assertTrue(isinstance(list(self.avatar.effects)[0], pickup_created.EFFECT))
self.assertEqual(list(self.avatar.effects)[0]._time_remaining, 5)
self.assertEqual(self.avatar.attack_strength, 11)

# Run 5 more turns and expect the effect to expire.
for i in range(5):
self.game.simulation_runner.run_single_turn(
loop.run_until_complete(self.game.simulation_runner.run_single_turn(
self.game.avatar_manager.get_player_id_to_serialised_action()
)
))

self.assertEqual(len(self.avatar.effects), 0)
self.assertEqual(self.avatar.attack_strength, 1)
Expand All @@ -67,16 +69,17 @@ def test_single_invulnerability_pickup_pickup_expiry(self):
self.assertEqual(self.avatar.resistance, 0)

# Avatar moves EAST to (1,0) where pickup is located, then repeats it 5 times.
loop = asyncio.get_event_loop()
for i in range(6):
self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action())
loop.run_until_complete(self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action()))

self.assertTrue(isinstance(list(self.avatar.effects)[0], pickup_created.EFFECT))
self.assertEqual(list(self.avatar.effects)[0]._time_remaining, 5)
self.assertEqual(self.avatar.resistance, INVULNERABILITY_RESISTANCE)

# Run 5 more turns and expect the effect to expire.
for i in range(5):
self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action())
loop.run_until_complete(self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action()))

self.assertEqual(len(self.avatar.effects), 0)
self.assertEqual(self.avatar.resistance, 0)
Expand All @@ -97,9 +100,10 @@ def test_multiple_damage_boost_pickup_expiry(self):
self.assertEqual(self.avatar.attack_strength, 1)

# Avatar moves EAST to (1,0) where pickup one is located.
self.game.simulation_runner.run_single_turn(
loop = asyncio.get_event_loop()
loop.run_until_complete(self.game.simulation_runner.run_single_turn(
self.game.avatar_manager.get_player_id_to_serialised_action()
)
))

self.assertTrue(isinstance(list(self.avatar.effects)[0], pickup_created_one.EFFECT))
self.assertEqual(len(self.avatar.effects), 1)
Expand All @@ -108,29 +112,29 @@ def test_multiple_damage_boost_pickup_expiry(self):

# Move twice to the second pickup.
for i in range(2):
self.game.simulation_runner.run_single_turn(
loop.run_until_complete(self.game.simulation_runner.run_single_turn(
self.game.avatar_manager.get_player_id_to_serialised_action()
)
))

self.assertTrue(isinstance(list(self.avatar.effects)[1], pickup_created_two.EFFECT))
self.assertEqual(len(self.avatar.effects), 2)
self.assertEqual(self.avatar.attack_strength, 26)

# Eight turns later, we expect the first effect to expire.
for i in range(8):
self.game.simulation_runner.run_single_turn(
loop.run_until_complete(self.game.simulation_runner.run_single_turn(
self.game.avatar_manager.get_player_id_to_serialised_action()
)
))

self.assertEqual(len(self.avatar.effects), 1)
self.assertEqual(list(self.avatar.effects)[0]._time_remaining, 2)
self.assertEqual(self.avatar.attack_strength, 16)

# Two turns later, the second pickup expires too.
for i in range(2):
self.game.simulation_runner.run_single_turn(
loop.run_until_complete(self.game.simulation_runner.run_single_turn(
self.game.avatar_manager.get_player_id_to_serialised_action()
)
))

self.assertEqual(len(self.avatar.effects), 0)
self.assertEqual(self.avatar.attack_strength, 1)
Expand All @@ -151,9 +155,10 @@ def test_multiple_invulnerability_boost_pickup_expiry(self):
self.assertEqual(self.avatar.resistance, 0)

# Avatar moves EAST to (1,0) where pickup one is located.
self.game.simulation_runner.run_single_turn(
loop = asyncio.get_event_loop()
loop.run_until_complete(self.game.simulation_runner.run_single_turn(
self.game.avatar_manager.get_player_id_to_serialised_action()
)
))

self.assertTrue(isinstance(list(self.avatar.effects)[0], pickup_created_one.EFFECT))
self.assertEqual(len(self.avatar.effects), 1)
Expand All @@ -162,29 +167,29 @@ def test_multiple_invulnerability_boost_pickup_expiry(self):

# Move twice to the second pickup.
for i in range(2):
self.game.simulation_runner.run_single_turn(
loop.run_until_complete(self.game.simulation_runner.run_single_turn(
self.game.avatar_manager.get_player_id_to_serialised_action()
)
))

self.assertTrue(isinstance(list(self.avatar.effects)[1], pickup_created_two.EFFECT))
self.assertEqual(len(self.avatar.effects), 2)
self.assertEqual(self.avatar.resistance, INVULNERABILITY_RESISTANCE * 2)

# Eight turns later, we expect the first effect to expire.
for i in range(8):
self.game.simulation_runner.run_single_turn(
loop.run_until_complete(self.game.simulation_runner.run_single_turn(
self.game.avatar_manager.get_player_id_to_serialised_action()
)
))

self.assertEqual(len(self.avatar.effects), 1)
self.assertEqual(list(self.avatar.effects)[0]._time_remaining, 2)
self.assertEqual(self.avatar.resistance, INVULNERABILITY_RESISTANCE)

# Two turns later, the second pickup expires too.
for i in range(2):
self.game.simulation_runner.run_single_turn(
loop.run_until_complete(self.game.simulation_runner.run_single_turn(
self.game.avatar_manager.get_player_id_to_serialised_action()
)
))

self.assertEqual(len(self.avatar.effects), 0)
self.assertEqual(self.avatar.resistance, 0)
10 changes: 6 additions & 4 deletions aimmo-game/tests/functional/test_health_pickups_and_effects.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import random
from unittest import TestCase
import asyncio

from hypothesis import given
import hypothesis.strategies as st
Expand All @@ -22,14 +23,15 @@ def setUp(self):
self.game.game_state.add_avatar(1, Location(0, 0))
self.cell = self.game.game_state.world_map.get_cell(Location(1, 0))
self.initial_health = self.game.avatar_manager.get_avatar(1).health
self.loop = asyncio.get_event_loop()

def test_health_pickups_and_effects_apply_default(self):
"""
HealthPickups without any parameter provided.
"""
self.cell.pickup = HealthPickup(self.cell)

self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action())
self.loop.run_until_complete(self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action()))

self.assertEqual(self.cell.avatar, self.game.avatar_manager.get_avatar(1))
self.assertEqual(self.cell.avatar.health, self.initial_health +
Expand All @@ -43,7 +45,7 @@ def test_health_pickups_and_effects_apply_custom_integers(self, restore_value):
self.setUp()
self.cell.pickup = HealthPickup(self.cell, restore_value)

self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action())
self.loop.run_until_complete(self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action()))
self.assertEqual(self.cell.avatar, self.game.avatar_manager.get_avatar(1))

if self.initial_health + restore_value > HEALTH_RESTORE_MAX:
Expand All @@ -61,7 +63,7 @@ def test_health_pickups_and_effects_apply_custom_floats(self, restore_value):
self.setUp()
self.cell.pickup = HealthPickup(self.cell, restore_value)

self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action())
self.loop.run_until_complete(self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action()))
self.assertEqual(self.cell.avatar, self.game.avatar_manager.get_avatar(1))

if self.initial_health + restore_value > HEALTH_RESTORE_MAX:
Expand All @@ -81,7 +83,7 @@ def test_health_effect_is_capped_at_HEALTH_RESTORE_MAX(self, restore_value):
self.setUp()
self.cell.pickup = HealthPickup(self.cell, restore_value)

self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action())
self.loop.run_until_complete(self.game.simulation_runner.run_single_turn(self.game.avatar_manager.get_player_id_to_serialised_action()))

self.assertEqual(self.cell.avatar, self.game.avatar_manager.get_avatar(1))
self.assertEqual(self.cell.avatar.health, AVATAR_HEALTH_MAX)
Loading