Files
snake-python/server/blueprints/battlesnake.py
T
daniel156161 f4c0ad193e
Build and Push Docker Container / build-and-push (push) Successful in 4m8s
only allow access to start, move, end endpoint with the user agent BattlesnakeEngine
2026-04-07 02:34:31 +02:00

111 lines
4.2 KiB
Python

from typing import TYPE_CHECKING, cast
import json, time, os
from quart import Blueprint, request, jsonify
from quart_common.web.decorators import require_user_agent
from quart_common.web.logger import await_log
from server.database import StorageLoader
from snakes import DEFAULT_SNAKE_CONFIG
from server.GameBoard import GameBoard
from server.Files import read_file
if TYPE_CHECKING:
from server.Server import Server
def create_battlesnake_blueprint(server:'Server') -> Blueprint:
blueprint = Blueprint('battlesnake', __name__)
async def _override_snake_config_with_environment_variables(config:dict[str, str]) -> dict[str, str]:
print(config)
config['version'] = server.snake_version
for key in ('author', 'color', 'head', 'tail'):
value = os.environ.get(f'SNAKE_{key.upper()}')
if value is not None:
config[key] = value
version_override = os.environ.get('SNAKE_VERSION')
if version_override is not None:
config['version'] = version_override
return config
@blueprint.get('/')
async def on_info():
server.metrics_collector.record_http_request('info')
snake_config = cast(dict[str, str]|None, await read_file(server.config_file, json.load))
if not snake_config:
snake_json = await _override_snake_config_with_environment_variables(DEFAULT_SNAKE_CONFIG)
else:
snake_json = await _override_snake_config_with_environment_variables(snake_config)
await await_log(server.logger.info(f'INFO Snake: {snake_json}'))
return snake_json
@blueprint.post('/start')
@require_user_agent("BattlesnakeEngine", abort_code=404)
async def on_start():
server.metrics_collector.record_http_request('start')
await server.game_runtime.prune_stale_games()
game_state = await request.get_json()
game_board = await server.game_runtime.create_game_board(game_state)
await server.gameplay_tracking.record_gameplay_start(game_state, game_board)
await await_log(server.logger.info(f'GAME START: {game_state['game']}'))
return 'ok'
@blueprint.post('/move')
@require_user_agent("BattlesnakeEngine", abort_code=404)
async def on_move():
server.metrics_collector.record_http_request('move')
game_state = await request.get_json()
move_started = time.perf_counter()
game_board = cast(GameBoard, await server.game_runtime.get_game_board(game_state))
next_move = game_board.snake_neat_make_a_move()
await server.game_runtime.persist_game_board(game_state['game']['id'], game_board)
await server.gameplay_tracking.record_gameplay_turn(game_state, next_move, game_board)
elapsed_ms = (time.perf_counter() - move_started) * 1000.0
await server.metrics_collector.record_move(next_move, elapsed_ms)
if server.debug:
await await_log(server.logger.debug(f'TURN: {game_state['turn']:3}, MOVE: {next_move:5}'))
return {'move': next_move}
@blueprint.post('/end')
@require_user_agent("BattlesnakeEngine", abort_code=404)
async def on_end():
server.metrics_collector.record_http_request('end')
await server.game_runtime.prune_stale_games()
game_state = await request.get_json()
if server.store_game_state:
game_board = cast(GameBoard, await server.game_runtime.get_game_board(game_state, end=True))
if server.check_tls_security:
await game_board.save(
StorageLoader.build(server.storage_type),
file_path=os.path.join(server.data_path, 'data'),
database=os.getenv('EDGEDB_DATABASE', None),
tls_security=None,
)
else:
await game_board.save(
StorageLoader.build(server.storage_type),
file_path=os.path.join(server.data_path, 'data'),
database=os.getenv('EDGEDB_DATABASE', None),
)
await server.gameplay_tracking.record_gameplay_end(game_state)
await server.dashboard_query.push_dashboard_games_update(game_state)
await await_log(server.logger.info(f'GAME ENDED: Winner is {[x['name'] for x in game_state['board']['snakes']]}'))
await server.game_runtime.delete_game_board(game_state)
await server.metrics_collector.record_game_end(game_state)
return 'ok'
# @blueprint.get('/cleanup')
# async def cleanup():
# results = server._cleanup_database()
# return jsonify(data=json.loads(results), status=200)
return blueprint