def _change_password(sio: ServerApp, session: GameSession, password: str): _verify_has_admin(sio, session.id, None) session.password = _hash_password(password) logger().info(f"{_describe_session(session)}: Changing password.") session.save() _add_audit_entry(sio, session, f"Changed password")
def _finish_session(sio: ServerApp, session: GameSession): _verify_has_admin(sio, session.id, None) if session.state != GameSessionState.IN_PROGRESS: raise InvalidAction("Session is not in progress") session.state = GameSessionState.FINISHED session.save()
def _finish_session(sio: ServerApp, session: GameSession): _verify_has_admin(sio, session.id, None) if session.state != GameSessionState.IN_PROGRESS: raise InvalidAction("Session is not in progress") session.state = GameSessionState.FINISHED logger().info(f"{_describe_session(session)}: Finishing session.") session.save() _add_audit_entry(sio, session, f"Finished session")
def _change_title(sio: ServerApp, session: GameSession, title: str): _verify_has_admin(sio, session.id, None) old_name = session.name session.name = title logger().info( f"{_describe_session(session)}: Changed name from {old_name}.") session.save() _add_audit_entry(sio, session, f"Changed name from {old_name} to {title}")
def _duplicate_session(sio: ServerApp, session: GameSession, new_title: str): _verify_has_admin(sio, session.id, None) current_user = sio.get_current_user() _add_audit_entry(sio, session, f"Duplicated session as {new_title}") with database.db.atomic(): new_session: GameSession = GameSession.create( name=new_title, password=session.password, creator=current_user, layout_description_json=session.layout_description_json, seed_hash=session.seed_hash, dev_features=session.dev_features, ) for preset in session.presets: assert isinstance(preset, GameSessionPreset) GameSessionPreset.create( session=new_session, row=preset.row, preset=preset.preset, ) GameSessionMembership.create( user=current_user, session=new_session, row=None, admin=True, connection_state="Offline", ) GameSessionAudit.create( session=new_session, user=current_user, message=f"Duplicated from {session.name}", )
def _update_layout_generation(sio: ServerApp, session: GameSession, active: bool): _verify_has_admin(sio, session.id, None) _verify_in_setup(session) if active: if session.generation_in_progress is None: session.generation_in_progress = sio.get_current_user() else: raise InvalidAction( f"Generation already in progress by {session.generation_in_progress.name}." ) else: session.generation_in_progress = None session.save()
def _start_session(sio: ServerApp, session: GameSession): _verify_has_admin(sio, session.id, None) _verify_in_setup(session) if session.layout_description_json is None: raise InvalidAction("Unable to start session, no game is available.") num_players = GameSessionMembership.select().where( GameSessionMembership.session == session, GameSessionMembership.row != None).count() expected_players = session.num_rows if num_players != expected_players: raise InvalidAction( f"Unable to start session, there are {num_players} but expected {expected_players} " f"({session.num_rows} x {session.num_teams}).") session.state = GameSessionState.IN_PROGRESS session.save()
def _change_layout_description(sio: ServerApp, session: GameSession, description_json: Optional[dict]): _verify_has_admin(sio, session.id, None) _verify_in_setup(session) rows_to_update = [] if description_json is None: description = None else: if session.generation_in_progress != sio.get_current_user(): if session.generation_in_progress is None: raise InvalidAction(f"Not waiting for a layout.") else: raise InvalidAction( f"Waiting for a layout from {session.generation_in_progress.name}." ) _verify_no_layout_description(session) description = LayoutDescription.from_json_dict(description_json) if description.player_count != session.num_rows: raise InvalidAction( f"Description is for a {description.player_count} players," f" while the session is for {session.num_rows}.") for permalink_preset, preset_row in zip(description.all_presets, session.presets): preset_row = typing.cast(GameSessionPreset, preset_row) if _get_preset(json.loads( preset_row.preset)).get_preset() != permalink_preset: preset = VersionedPreset.with_preset(permalink_preset) if preset.game not in session.allowed_games: raise InvalidAction(f"{preset.game} preset not allowed.") preset_row.preset = json.dumps(preset.as_json) rows_to_update.append(preset_row) with database.db.atomic(): for preset_row in rows_to_update: preset_row.save() session.generation_in_progress = None session.layout_description = description session.save() _add_audit_entry( sio, session, "Removed generated game" if description is None else f"Set game to {description.shareable_word_hash}")
def admin_session(user, session_id): session: GameSession = GameSession.get_by_id(session_id) rows = [] presets = session.all_presets for player in session.players: player = typing.cast(GameSessionMembership, player) if player.is_observer: rows.append([ player.effective_name, "Observer", "", ]) else: preset = presets[player.row] db = default_database.resource_database_for(preset.game) inventory = [] if player.inventory is not None: try: parsed_inventory: list[dict] = BinaryInventory.parse( player.inventory) except construct.ConstructError: # Handle old format in an adhoc way # TODO 4.3: remove this code and purge all old inventory from the server db items_by_id: dict[int, ItemResourceInfo] = { item.extra["item_id"]: item for item in db.item } parsed_inventory = [{ "name": items_by_id[item["index"]].short_name, **item, } for item in OldBinaryInventory.parse( player.inventory)] for item in parsed_inventory: if item["amount"] + item["capacity"] > 0: inventory.append("{} x{}/{}".format( db.get_item(item["name"]).long_name, item["amount"], item["capacity"])) rows.append([ player.effective_name, preset.name, ", ".join(inventory), ]) header = ["Name", "Preset", "Inventory"] return "<table border='1'><tr>{}</tr>{}</table>".format( "".join(f"<th>{h}</th>" for h in header), "".join("<tr>{}</tr>".format("".join(f"<td>{h}</td>" for h in r)) for r in rows), )
def _change_layout_description(sio: ServerApp, session: GameSession, description_json: Optional[dict]): _verify_has_admin(sio, session.id, None) _verify_in_setup(session) rows_to_update = [] if description_json is None: description = None else: if session.generation_in_progress != sio.get_current_user(): if session.generation_in_progress is None: raise InvalidAction(f"Not waiting for a layout.") else: raise InvalidAction( f"Waiting for a layout from {session.generation_in_progress.name}." ) _verify_no_layout_description(session) description = LayoutDescription.from_json_dict(description_json) permalink = description.permalink if permalink.player_count != session.num_rows: raise InvalidAction( f"Description is for a {permalink.player_count} players," f" while the session is for {session.num_rows}.") for permalink_preset, preset_row in zip(permalink.presets.values(), session.presets): preset_row = typing.cast(GameSessionPreset, preset_row) if _get_preset(json.loads( preset_row.preset)).get_preset() != permalink_preset: preset = VersionedPreset.with_preset(permalink_preset) if preset.game != RandovaniaGame.PRIME2: raise InvalidAction("Only Prime 2 presets allowed.") preset_row.preset = json.dumps(preset.as_json) rows_to_update.append(preset_row) with database.db.atomic(): for preset_row in rows_to_update: preset_row.save() session.generation_in_progress = None session.layout_description = description session.save()
def game_session_request_update(sio: ServerApp, session_id: int): current_user = sio.get_current_user() session: GameSession = GameSession.get_by_id(session_id) membership = GameSessionMembership.get_by_ids(current_user.id, session_id) _emit_session_meta_update(session) if session.layout_description is not None: _emit_session_actions_update(session) if not membership.is_observer and session.state != GameSessionState.SETUP: _emit_game_session_pickups_update(sio, membership) _emit_session_audit_update(session)
def admin_sessions(user): lines = [] for session in GameSession.select().order_by( GameSession.creation_date): lines.append( "<tr><td><a href='{}'>{}</a></td><td>{}</td><td>{}</td><td>{}</td><td>{}</td></tr>" .format( flask.url_for('admin_session', session_id=session.id), session.name, session.creator.name, session.creation_date, session.state, len(session.players), )) return ( "<table border='1'>" "<tr><th>Name</th><th>Creator</th><th>Creation Date</th><th>State</th><th>Num Players</th></tr>" "{}</table>").format("".join(lines))
def admin_sessions(user): paginated_query = flask_utils.PaginatedQuery( GameSession.select().order_by(GameSession.creation_date.desc()), paginate_by=20, check_bounds=True, ) lines = [] for session in paginated_query.get_object_list(): lines.append( "<tr><td><a href='{}'>{}</a></td><td>{}</td><td>{}</td><td>{}</td><td>{}</td></tr>" .format( flask.url_for('admin_session', session_id=session.id), session.name, session.creator.name, session.creation_date, session.state, len(session.players), )) page = paginated_query.get_page() previous = "Previous" if page > 1: previous = "<a href='{}'>Previous</a>".format( flask.url_for(".admin_sessions", page=page - 1)) next_link = "Next" if page < paginated_query.get_page_count(): next_link = "<a href='{}'>Next</a>".format( flask.url_for(".admin_sessions", page=page + 1)) return ( "<table border='1'>" "<tr><th>Name</th><th>Creator</th><th>Creation Date</th><th>State</th><th>Num Players</th></tr>" "{content}</table>Page {page} of {num_pages}. {previous} / {next}." ).format( content="".join(lines), page=page, num_pages=paginated_query.get_page_count(), previous=previous, next=next_link, )
def create_game_session(sio: ServerApp, session_name: str): current_user = sio.get_current_user() with database.db.atomic(): new_session = GameSession.create( name=session_name, password=None, creator=current_user, ) GameSessionPreset.create( session=new_session, row=0, preset=json.dumps(PresetManager(None).default_preset.as_json)) membership = GameSessionMembership.create(user=sio.get_current_user(), session=new_session, row=0, admin=True) sio.join_game_session(membership) return new_session.create_session_entry()
def join_game_session(sio: ServerApp, session_id: int, password: Optional[str]): session = GameSession.get_by_id(session_id) if session.password is not None: if password is None or _hash_password(password) != session.password: raise WrongPassword() elif password is not None: raise WrongPassword() membership = GameSessionMembership.get_or_create( user=sio.get_current_user(), session=session, defaults={ "row": None, "admin": False })[0] _emit_session_update(session) sio.join_game_session(membership) return session.create_session_entry()
def admin_session(user, session_id): session: GameSession = GameSession.get_by_id(session_id) rows = [] presets = session.all_presets for player in session.players: player = typing.cast(GameSessionMembership, player) if player.is_observer: rows.append([ player.effective_name, "Observer", "", ]) else: preset = presets[player.row] db = default_database.resource_database_for(preset.game) inventory = [] for item in json.loads(player.inventory): if item["amount"] + item["capacity"] > 0: inventory.append("{} x{}/{}".format( db.get_item(item["index"]).long_name, item["amount"], item["capacity"])) rows.append([ player.effective_name, preset.name, ", ".join(inventory), ]) header = ["Name", "Preset", "Inventory"] return "<table border='1'><tr>{}</tr>{}</table>".format( "".join(f"<th>{h}</th>" for h in header), "".join("<tr>{}</tr>".format("".join(f"<td>{h}</td>" for h in r)) for r in rows), )
def _emit_session_meta_update(session: GameSession): flask_socketio.emit("game_session_meta_update", session.create_session_entry(), room=f"game-session-{session.id}", namespace="/")
def list_game_sessions(sio: ServerApp, limit: Optional[int]): return [ session.create_list_entry() for session in GameSession.select().order_by(GameSession.id.desc()).limit(limit) ]
def _change_password(sio: ServerApp, session: GameSession, password: str): _verify_has_admin(sio, session.id, None) session.password = _hash_password(password) session.save()
def _emit_session_audit_update(session: GameSession): flask_socketio.emit("game_session_audit_update", session.get_audit_log(), room=f"game-session-{session.id}", namespace="/")
def _emit_session_actions_update(session: GameSession): flask_socketio.emit("game_session_actions_update", session.describe_actions(), room=f"game-session-{session.id}", namespace="/")
def list_game_sessions(sio: ServerApp): return [session.create_list_entry() for session in GameSession.select()]
def _change_password(sio: ServerApp, session: GameSession, password: str): _verify_has_admin(sio, session.id, None) session.password = _hash_password(password) logger().info(f"Session {session.id}: Changing password.") session.save()