Skip to content

Commit

Permalink
Mission point update calculations (#9)
Browse files Browse the repository at this point in the history
* Overhauled mission_update point calcs.

* Added state overwrite test endpoint.

* Logged response content on unsuccessful http call.
  • Loading branch information
sei-jvessella authored Nov 14, 2022
1 parent a89bf33 commit 54a9093
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 30 deletions.
2 changes: 2 additions & 0 deletions curl_test_endpoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ curl --header "x-api-key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" localhost:8000/test/
curl --header "x-api-key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" localhost:8000/test/mark_challenge_task_complete/{team_id}/{task_id}

curl --header "x-api-key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" localhost:8000/test/mark_codexes_complete/{team_id}

curl --header "x-api-key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" http://localhost:8000/test/overwrite_current_state
18 changes: 12 additions & 6 deletions gamebrain/admin/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ async def get_team_from_db(team_id: TeamID) -> dict:


async def register_gamespace_and_get_vms(
game_id: GameID, team_id: TeamID
game_id: GameID, team_id: TeamID, total_points: int
) -> (GamespaceID, GamespaceExpiration, list[ConsoleUrl]):
gameboard_team = await gameboard.get_team(team_id)
if not gameboard_team:
Expand All @@ -146,7 +146,10 @@ async def register_gamespace_and_get_vms(

external_id = game_specs["externalId"]
gamespace = await topomojo.register_gamespace(
external_id, gameboard_team["sessionEnd"], gameboard_team["members"]
external_id,
gameboard_team["sessionEnd"],
gameboard_team["members"],
total_points,
)
if not gamespace or "vms" not in gamespace:
logging.error(
Expand Down Expand Up @@ -188,6 +191,8 @@ async def deploy(
async with TeamLocks(team_id):
team_data = await get_team_from_db(team_id)

total_points = await GameStateManager.get_total_points()

gamespace_id = team_data.get("gamespace_id")
headless_url = team_data.get("headless_url")
vm_data = team_data.get("vm_data", [])
Expand All @@ -205,15 +210,15 @@ async def deploy(
gamespaceId=None,
headlessUrl=None,
vms=[],
totalPoints=get_settings().game.total_points,
totalPoints=total_points,
)

try:
(
gamespace_id,
gamespace_expiration,
console_urls,
) = await register_gamespace_and_get_vms(game_id, team_id)
) = await register_gamespace_and_get_vms(game_id, team_id, total_points)
team_name = await get_team_name(game_id, team_id)
except Exception as e:
# If anything goes wrong with gamespace deployment,
Expand Down Expand Up @@ -242,7 +247,7 @@ async def deploy(
gamespaceId=gamespace_id,
headlessUrl=headless_url,
vms=console_urls,
totalPoints=get_settings().game.total_points,
totalPoints=total_points,
)


Expand All @@ -267,6 +272,7 @@ class ActiveTeamsResponse(BaseModel):
@admin_router.get("/teams_active")
async def get_teams_active() -> ActiveTeamsResponse:
teams = await get_teams()
total_points = await GameStateManager.get_total_points()
active_teams = {}
for team in teams:
gamespace_id = team.get("gamespace_id")
Expand All @@ -280,7 +286,7 @@ async def get_teams_active() -> ActiveTeamsResponse:
gamespaceId=gamespace_id,
headlessUrl=headless_url,
vms=console_urls,
totalPoints=get_settings().game.total_points,
totalPoints=total_points,
)

response = ActiveTeamsResponse(__root__=active_teams)
Expand Down
1 change: 1 addition & 0 deletions gamebrain/clients/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ async def _service_request_and_log(
f"HTTP Method was: {request.method}\n"
f"Headers were: {request.headers}\n"
f"Request Body was: {request.content}\n"
f"Response content was: {response.content}\n"
)

return response
4 changes: 2 additions & 2 deletions gamebrain/clients/topomojo.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ async def poll_dispatch(dispatch_id: str) -> Optional[Any]:


async def register_gamespace(
workspace_id: str, expiration_time: str, team_members: List[Dict]
workspace_id: str, expiration_time: str, team_members: List[Dict], total_points: int
):
"""
team_members: Each dict contains 'id' and 'approvedName' keys.
Expand All @@ -109,7 +109,7 @@ async def register_gamespace(
"playerCount": len(team_members),
"maxAttempts": 3,
"maxMinutes": settings.game.gamespace_duration_minutes,
"points": settings.game.total_points,
"points": total_points,
"allowReset": True,
"allowPreview": True,
"startGamespace": True,
Expand Down
1 change: 0 additions & 1 deletion gamebrain/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ class GameSettingsModel(BaseModel):

gamestate_test_mode: Optional[bool] = False
game_id: str
total_points: int = 1000

headless_client_urls: dict[Hostname, ServerPublicUrl]

Expand Down
28 changes: 10 additions & 18 deletions gamebrain/gamedata/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,27 +433,24 @@ def _complete_task_and_unlock_next(
"exist in the global data."
)
else:
total_points = cls._settings.game.total_points
mission_count = len(cls._cache.mission_map.__root__)
mission_points = total_points // mission_count
task = asyncio.create_task(
gameboard.mission_update(
team_id,
global_mission.missionID,
global_mission.title,
mission_points,
global_mission.points,
)
)
task.add_done_callback(
lambda _: logging.info(
f"Team {team_id} completed mission "
f"{global_mission.missionID} and was awarded {mission_points} points."
f"{global_mission.missionID} and was awarded {global_mission.points} points."
)
)

team_data.session.teamCodexCount = sum(
(
1 if mission.complete else 0
1 if mission.complete and not global_mission.isSpecial else 0
for mission in team_data.missions.values()
)
)
Expand Down Expand Up @@ -544,6 +541,13 @@ def _basic_validation(cls, initial_state: GameDataCacheSnapshot):
"which is not in the location map."
)

@classmethod
async def get_total_points(cls) -> int:
async with cls._lock:
return sum(
map(lambda m: m.points, cls._cache.mission_map.__root__.values())
)

@classmethod
async def snapshot_data(cls) -> JsonStr:
async with cls._lock:
Expand Down Expand Up @@ -1008,18 +1012,6 @@ async def jump(cls, team_id: TeamID, location_id: LocationID) -> GenericResponse
team_db_data = await get_team(team_id)
gamespace_id = team_db_data.get("gamespace_id")

total_points = cls._settings.game.total_points
mission_count = len(cls._cache.mission_map.__root__)
# Each mission gets total_points // mission_count,
# so the final goal should get whatever points remain.
final_goal_total_points = total_points - (
(total_points // mission_count) * (mission_count - 1)
)

await gameboard.mission_update(
team_id, "finalGoal", "Final Goal", final_goal_total_points
)

if not gamespace_id:
logging.error(
f"Team {team_id} tried to unlock the final destination, "
Expand Down
1 change: 1 addition & 0 deletions gamebrain/gamedata/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class MissionData(BaseModel):
isSpecial: bool = False
roleList: list[str]
taskList: list[TaskDataIdentifierStub]
points: int = 0


class MissionDataTeamSpecific(BaseModel):
Expand Down
34 changes: 32 additions & 2 deletions gamebrain/test_endpoints.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from json import JSONDecodeError, load as json_load
import logging

from fastapi import APIRouter, Depends, HTTPException
from pydantic import ValidationError

from .admin.controller import get_teams_active
from .auth import admin_api_key_dependency
from .config import get_settings, SettingsModel
from .clients import topomojo
from .gamedata.cache import (
GameDataCacheSnapshot,
GameStateManager,
GamespaceStateOutput,
GameDataResponse,
Expand Down Expand Up @@ -148,8 +152,8 @@ async def test_complete_power_change(

@test_router.get("/complete/unlock/{coordinates}/{team_id}")
async def test_unlock_location(
coordinates: str,
team_id: TeamID,
coordinates: str,
team_id: TeamID,
) -> LocationUnlockResponse:
try:
return await GameStateManager.unlock_location(team_id, coordinates)
Expand All @@ -160,3 +164,29 @@ async def test_unlock_location(
@test_router.get("/settings")
async def test_get_settings() -> SettingsModel:
return get_settings()


@test_router.get("/overwrite_current_state")
async def test_overwrite_current_state():
active_teams = await get_teams_active()
if active_teams.__root__:
raise HTTPException(
status_code=400, detail="There are active teams. Will not overwrite."
)

with open("initial_state.json") as f:
try:
new_state_data = json_load(f)
except JSONDecodeError as e:
raise HTTPException(
status_code=400, detail=f"JSON has invalid formatting: {e}"
)

try:
new_state = GameDataCacheSnapshot(**new_state_data)
except ValidationError as e:
raise HTTPException(
status_code=400, detail=f"Could not validate new cache state: {e}"
)

await GameStateManager.init(new_state, GameStateManager._settings)
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ wrapt==1.14.1

pytest~=7.1.2
pytest-asyncio~=0.19.0
yappi~=1.3.6
yappi~=1.3.6

0 comments on commit 54a9093

Please sign in to comment.