diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -1,4 +1,4 @@ -from flask import Flask, render_template_string, request, redirect, url_for, session +from flask import Flask, render_template_string, request, redirect, url_for, session, jsonify import random import string import json @@ -10,26 +10,23 @@ import threading from datetime import datetime from huggingface_hub import HfApi, hf_hub_download from huggingface_hub.utils import RepositoryNotFoundError +from urllib.parse import urlparse, parse_qs app = Flask(__name__) app.config['SECRET_KEY'] = 'your-very-secret-key-here' -socketio = SocketIO(app) +socketio = SocketIO(app, async_mode='threading') -# --- Hugging Face Hub Settings --- -REPO_ID = "flpolprojects/Clients" # ваш репозиторий -HF_TOKEN_WRITE = os.getenv("HF_TOKEN") # токен с правами на запись -HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") # токен с правами на чтение (можно тот же, если у него есть и чтение, и запись) +REPO_ID = "flpolprojects/Clients" +HF_TOKEN_WRITE = os.getenv("HF_TOKEN") +HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") -# --- File Paths --- ROOMS_DB = os.path.join(app.root_path, 'rooms.json') USERS_DB = os.path.join(app.root_path, 'users.json') GAMES_DB = os.path.join(app.root_path, 'games.json') -# --- Data Loading and Saving with Hugging Face Backup --- - def load_json(file_path, default={}): try: - download_db_from_hf() # Скачиваем перед загрузкой + download_db_from_hf() if os.path.exists(file_path): with open(file_path, 'r', encoding='utf-8') as f: return json.load(f) @@ -38,25 +35,24 @@ def load_json(file_path, default={}): print(f"Ошибка загрузки JSON из {file_path}: {e}") return default except Exception as e: - print(f"Непредвиденная ошибка при загрузке: {e}") #Добавил обработку + print(f"Непредвиденная ошибка при загрузке: {e}") return default def save_json(file_path, data): try: with open(file_path, 'w', encoding='utf-8') as f: json.dump(data, f, indent=4, ensure_ascii=False) - upload_db_to_hf() # Загружаем после сохранения + upload_db_to_hf() except OSError as e: print(f"Ошибка сохранения JSON в {file_path}: {e}") - except Exception as e: #Добавил обработку + except Exception as e: print(f"Непредвиденная ошибка при сохранении: {e}") def upload_db_to_hf(): try: api = HfApi() - # Загружаем все три JSON файла for file_path, repo_path in [(ROOMS_DB, "rooms.json"), (USERS_DB, "users.json"), (GAMES_DB, "games.json")]: - if os.path.exists(file_path): # Проверяем наличие файла + if os.path.exists(file_path): api.upload_file( path_or_fileobj=file_path, path_in_repo=repo_path, @@ -65,16 +61,12 @@ def upload_db_to_hf(): token=HF_TOKEN_WRITE, commit_message=f"Backup: {repo_path} ({datetime.now().strftime('%Y-%m-%d %H:%M:%S')})" ) - print(f"Файл {file_path} успешно загружен на Hugging Face Hub.") - else: - print(f"Файл {file_path} не существует, пропуск загрузки.") except Exception as e: print(f"Ошибка при загрузке файлов на Hugging Face Hub: {e}") def download_db_from_hf(): try: api = HfApi() - # Скачиваем все три JSON файла for file_path, repo_path in [(ROOMS_DB, "rooms.json"), (USERS_DB, "users.json"), (GAMES_DB, "games.json")]: try: hf_hub_download( @@ -82,66 +74,56 @@ def download_db_from_hf(): filename=repo_path, repo_type="dataset", token=HF_TOKEN_READ, - local_dir=".", # Сохраняем в текущую директорию - local_dir_use_symlinks=False # Важно для корректной работы на некоторых платформах + local_dir=".", + local_dir_use_symlinks=False ) - print(f"Файл {repo_path} успешно скачан с Hugging Face Hub.") except RepositoryNotFoundError: - print(f"Файл {repo_path} не найден в репозитории. Создание локального.") - # Создаем пустой файл, если его нет в репозитории if not os.path.exists(file_path): with open(file_path, 'w') as f: - json.dump({}, f) # Создание пустого + json.dump({}, f) except Exception as e: print(f"Ошибка при скачивании файла {repo_path}: {e}") - - except Exception as e: print(f"Ошибка при скачивании файлов с Hugging Face Hub: {e}") def periodic_backup(): while True: upload_db_to_hf() - time.sleep(15) # Резервное копирование каждые 15 секунд - + time.sleep(15) -# --- Initial Data Loading --- rooms = load_json(ROOMS_DB) users = load_json(USERS_DB) games_data = load_json(GAMES_DB, default={ "crocodile": { "name": "Крокодил", - "description": "Один игрок (ведущий) получает слово и должен показать его жестами остальным игрокам, не произнося ни слова. Остальные игроки пытаются угадать слово.", + "description": "Один игрок показывает слово жестами.", "min_players": 2, - "max_players": 5, + "max_players": 10, "state": {} }, "alias": { "name": "Alias", - "description": "Один игрок (ведущий) получает слово и должен объяснить его другими словами, не используя однокоренные. Остальные игроки пытаются угадать слово.", + "description": "Один игрок объясняет слово.", "min_players": 2, - "max_players": 5, + "max_players": 10, "state": {} }, "mafia": { "name": "Мафия", - "description": "Игроки делятся на две команды: мафию и мирных жителей. Мафия пытается тайно убивать мирных жителей, а мирные жители пытаются вычислить и казнить мафию.", + "description": "Мафия против мирных жителей.", "min_players": 4, - "max_players": 5, # Можно увеличить, но для теста оставим так + "max_players": 10, "state": {} }, "durak": { "name": "Дурак", - "description": "Карточная игра, в которой игроки стараются избавиться от всех своих карт.", + "description": "Карточная игра.", "min_players": 2, - "max_players": 5, - "state": {} # Состояние игры + "max_players": 10, + "state": {} } }) -save_json(GAMES_DB, games_data) # Сохраняем изменения (на случай, если файл был создан) - - -# --- Helper Functions --- +save_json(GAMES_DB, games_data) def generate_token(): return ''.join(random.choices(string.ascii_letters + string.digits, k=15)) @@ -149,8 +131,21 @@ def generate_token(): def hash_password(password): return hashlib.sha256(password.encode('utf-8')).hexdigest() - -# --- Flask Routes --- +def get_youtube_id(url): + if not url: + return None + parsed_url = urlparse(url) + if parsed_url.hostname in ('www.youtube.com', 'youtube.com'): + if parsed_url.path == '/watch': + query = parse_qs(parsed_url.query) + return query.get('v', [None])[0] + elif parsed_url.path.startswith('/embed/'): + return parsed_url.path.split('/embed/')[1].split('?')[0] + elif parsed_url.path.startswith('/v/'): + return parsed_url.path.split('/v/')[1].split('?')[0] + elif parsed_url.hostname in ('youtu.be', 'www.youtu.be'): + return parsed_url.path[1:].split('?')[0] + return None @app.route('/', methods=['GET', 'POST']) def index(): @@ -165,18 +160,19 @@ def index(): if action == 'register': if username in users: return "Пользователь уже существует", 400 - users[username] = hash_password(password) + users[username] = {'password': hash_password(password), 'rooms': []} save_json(USERS_DB, users) session['username'] = username return redirect(url_for('dashboard')) elif action == 'login': - if username in users and users[username] == hash_password(password): + if username in users and users[username]['password'] == hash_password(password): session['username'] = username return redirect(url_for('dashboard')) return "Неверный логин или пароль", 401 - return render_template_string(''' + return render_template_string(''' + @@ -273,7 +269,8 @@ def index(): -''') + +''') @app.route('/dashboard', methods=['GET', 'POST']) @@ -281,20 +278,43 @@ def dashboard(): if 'username' not in session: return redirect(url_for('index')) + user = users.get(session['username']) + if not user: + session.pop('username', None) + return redirect(url_for('index')) + + if request.method == 'POST': action = request.form.get('action') if action == 'create': token = generate_token() - rooms[token] = {'users': [], 'max_users': 5, 'admin': session['username'], 'current_game': None} + rooms[token] = { + 'users': [session['username']], + 'max_users': 10, + 'admin': session['username'], + 'current_game': None, + 'guests': [], + 'youtube_url': None, + 'youtube_state': {'isPlaying': False, 'currentTime': 0, 'last_sync_time': time.time()}, + 'player_states': {} + } + users[session['username']]['rooms'].append(token) save_json(ROOMS_DB, rooms) + save_json(USERS_DB, users) return redirect(url_for('room', token=token)) elif action == 'join': token = request.form.get('token') - if token in rooms and len(rooms[token]['users']) < rooms[token]['max_users']: + if token in rooms and len(rooms[token]['users']) + len(rooms[token]['guests']) < rooms[token]['max_users']: + if session['username'] not in rooms[token]['users']: + rooms[token]['users'].append(session['username']) + users[session['username']]['rooms'].append(token) + save_json(ROOMS_DB, rooms) + save_json(USERS_DB, users) return redirect(url_for('room', token=token)) return "Комната не найдена или переполнена", 404 - return render_template_string(''' + return render_template_string(''' + @@ -400,1688 +420,735 @@ def dashboard(): -''', session=session) + +''', session=session) @app.route('/logout', methods=['POST']) def logout(): + username = session.get('username') + if username: + for room_token in list(users.get(username, {}).get('rooms', [])): + if room_token in rooms and (username in rooms[room_token]['users'] or username in rooms[room_token].get('player_states', {})): + if rooms[room_token]['admin'] == username: + del rooms[room_token] + save_json(ROOMS_DB, rooms) + socketio.emit('room_deleted', {'token': room_token}, room=room_token) + else: + if username in rooms[room_token]['users']: + rooms[room_token]['users'].remove(username) + if username in rooms[room_token]['player_states']: + del rooms[room_token]['player_states'][username] + save_json(ROOMS_DB, rooms) + socketio.emit('user_left', {'username': username}, room=room_token) + + if username in users and room_token in users[username]['rooms']: + users[username]['rooms'].remove(room_token) + save_json(USERS_DB, users) session.pop('username', None) return redirect(url_for('index')) + @app.route('/room/') def room(token): - if 'username' not in session: + if 'username' not in session and 'guest_id' not in session: return redirect(url_for('index')) if token not in rooms: return redirect(url_for('dashboard')) - is_admin = rooms[token]['admin'] == session['username'] + is_admin = rooms[token]['admin'] == session.get('username') current_game = rooms[token].get('current_game') + if 'username' in session: + username = session['username'] + is_guest = False + elif 'guest_id' in session: + username = session['guest_id'] + is_guest = True + if username not in rooms[token]['guests']: + rooms[token]['guests'].append(username) + save_json(ROOMS_DB, rooms) + else: + return redirect(url_for('guest_login', token=token)) + + return render_template_string(''' - Комната {{ token }} + Метавселенная: Комната {{ token }} - - - - -

Комната: {{ token }}

-
Пользователи: {% for user in rooms[token]['users'] %}{{ user }}{% if not loop.last %}, {% endif %}{% endfor %}
- -
-
- +
+ + +
+
+

Комната: {{ token }}

+

Пользователи:

+
-
-
-
-
- -
-

Доступные игры

-
- {% for game_id, game_info in games_data.items() %} - {% if rooms[token]['users']|length >= game_info.min_players and rooms[token]['users']|length <= game_info.max_players %} +
+

Игровая Зона

+
+ {% for game_id, game_info in games_data.items() %}

{{ game_info.name }}

{{ game_info.description }}

{% if is_admin %} - + {% endif %}
- {% endif %} - {% endfor %} + {% endfor %} +
+
+

+

+
+
+
+ +
+

Кинотеатр

+
+
+
+ {% if is_admin %} + + + {% else %} +

Админ управляет видео.

+ {% endif %} +
+
+
+ +
+

Используйте W, A, S, D для движения. Мышь для обзора.

- -
-

-

-
-
+ + + + -''', token=token, session=session, is_admin=is_admin, rooms=rooms, games_data=games_data) +''', token=token, session=session, is_admin=is_admin, rooms=rooms, games_data=games_data, username=username, is_guest=is_guest) + +@app.route('/join_as_guest/', methods=['GET']) +def join_as_guest(token): + if token not in rooms: + return "Комната не найдена", 404 + guest_id = 'Гость_' + generate_token()[:6] + session['guest_id'] = guest_id + if 'username' in session: + session.pop('username') + return redirect(url_for('room', token=token)) + +@app.route('/guest_login/') +def guest_login(token): + if token not in rooms: + return "Комната не найдена", 404 + return render_template_string(''' + Вход для гостей +

Вход в комнату {{ token }} как гость

+ Войти как гость + ''', token=token) + @socketio.on('join') def handle_join(data): token = data['token'] username = data['username'] - print(f"User {username} joining room {token}") - if token in rooms and len(rooms[token]['users']) < rooms[token]['max_users']: - join_room(token) - if username not in rooms[token]['users']: - rooms[token]['users'].append(username) - save_json(ROOMS_DB, rooms) - emit('user_joined', {'username': username, 'users': rooms[token]['users']}, room=token) - emit('init_users', {'users': rooms[token]['users']}, to=request.sid) - if rooms[token]['current_game']: # Если игра уже идет, отправляем новому пользователю текущее состояние - emit('game_started', {'game_id': rooms[token]['current_game']}, to=request.sid) - emit('update_game_state', {'game_id': rooms[token]['current_game'], 'state': games_data[rooms[token]['current_game']]['state'][token]}, to=request.sid); + is_guest = data.get('is_guest', False) + join_room(token) + + if token in rooms: + if is_guest: + if username not in rooms[token]['guests']: + rooms[token]['guests'].append(username) + else: + if username not in rooms[token]['users']: + rooms[token]['users'].append(username) + + initial_state = { + 'pos': {'x': random.uniform(-5, 5), 'y': 1.7, 'z': random.uniform(-5, 5)}, + 'rot': {'x': 0, 'y': 0, 'z': 0, 'w': 1} + } + rooms[token]['player_states'][username] = initial_state + save_json(ROOMS_DB, rooms) - else: - emit('error_message', {'message': 'Комната заполнена или не существует'}, to=request.sid) + emit('init_room_state', { + 'player_states': rooms[token]['player_states'], + 'users': rooms[token]['users'], + 'guests': rooms[token]['guests'] + }, to=request.sid) + + emit('user_joined', { + 'username': username, + 'initial_state': initial_state, + 'all_users': rooms[token]['users'], + 'all_guests': rooms[token]['guests'] + }, room=token, include_self=False) @socketio.on('leave') def handle_leave(data): token = data['token'] username = data['username'] - print(f"User {username} leaving room {token}") - if token in rooms and username in rooms[token]['users']: - leave_room(token) - rooms[token]['users'].remove(username) - if rooms[token]['admin'] == username: - # Если админ выходит и игра активна, завершаем игру - if rooms[token].get('current_game'): - game_id = rooms[token]['current_game'] - if games_data[game_id]['state'].get(token): - del games_data[game_id]['state'][token] - save_json(GAMES_DB, games_data) - del rooms[token] - save_json(ROOMS_DB, rooms) - emit('user_left', {'username': username, 'users': []}, room=token) # Важно уведомить всех, что комната больше не существует - return; - - if rooms[token].get('current_game'): # Если игра в процессе - game_id = rooms[token]['current_game'] - if games_data[game_id]['state'].get(token): - if game_id == 'crocodile': - if games_data[game_id]['state'][token].get('presenter') == username: - # Сброс игры, если уходит ведущий - del games_data[game_id]['state'][token] - emit('update_game_state', {'game_id': game_id, 'state': {}}, room=token); # Уведомляем, что состояние сброшено - save_json(GAMES_DB, games_data) - return - #Удаление догадок - if games_data[game_id]['state'][token].get('guesses'): - games_data[game_id]['state'][token]['guesses'] = [guess for guess in games_data[game_id]['state'][token]['guesses'] if guess['user'] != username] - - elif game_id == 'alias': - if games_data[game_id]['state'][token].get('presenter') == username: - del games_data[game_id]['state'][token] - emit('update_game_state', {'game_id': game_id, 'state': {}}, room=token) - save_json(GAMES_DB, games_data) - return - if games_data[game_id]['state'][token].get('guesses'): - games_data[game_id]['state'][token]['guesses'] = [guess for guess in games_data[game_id]['state'][token]['guesses'] if guess['user'] != username] - - elif game_id == 'mafia': - if games_data[game_id]['state'][token].get('roles') and username in games_data[game_id]['state'][token]['roles']: - del games_data[game_id]['state'][token]['roles'][username] - - if games_data[game_id]['state'][token].get('votes'): - #Удаляем голос, если он был - if username in games_data[game_id]['state'][token]['votes']: - del games_data[game_id]['state'][token]['votes'][username] - - elif game_id == 'durak': - if games_data[game_id]['state'][token].get('hands') and username in games_data[game_id]['state'][token]['hands']: - del games_data[game_id]['state'][token]['hands'][username] - - #Если ушел атакующий или защищающийся - if games_data[game_id]['state'][token].get('attacker') == username: - games_data[game_id]['state'][token]['attacker'] = None; - - if games_data[game_id]['state'][token].get('defender') == username: - games_data[game_id]['state'][token]['defender'] = None; - save_json(GAMES_DB, games_data); # Сохраняем изменения в любом случае - emit('update_game_state', {'game_id': game_id, 'state': games_data[game_id]['state'][token]}, room=token); + is_guest = data.get('is_guest', False) + leave_room(token) + + if token in rooms: + if is_guest and username in rooms[token]['guests']: + rooms[token]['guests'].remove(username) + elif not is_guest and username in rooms[token]['users']: + rooms[token]['users'].remove(username) + + if username in rooms[token]['player_states']: + del rooms[token]['player_states'][username] save_json(ROOMS_DB, rooms) - emit('user_left', {'username': username, 'users': rooms[token]['users']}, room=token) + emit('user_left', { + 'username': username, + 'users': rooms[token]['users'], + 'guests': rooms[token]['guests'] + }, room=token) + +@socketio.on('update_player_state') +def handle_update_player_state(data): + token = data['token'] + state = data['state'] + username = session.get('username') or session.get('guest_id') + if token in rooms and username: + rooms[token]['player_states'][username] = state + emit('player_state_updated', { + 'username': username, + 'state': state + }, room=token, include_self=False) @socketio.on('signal') def handle_signal(data): emit('signal', data, room=data['token'], include_self=False) -@socketio.on('admin_mute') -def handle_admin_mute(data): +@socketio.on('set_youtube_url') +def handle_set_youtube_url(data): token = data['token'] - target_user = data['targetUser'] - by_user = data['byUser'] - if token in rooms and rooms[token].get('admin') == by_user: - emit('admin_muted', {'targetUser': target_user}, room=token) + url = data['url'] + username = session.get('username') + if token in rooms and rooms[token].get('admin') == username: + video_id = get_youtube_id(url) + if video_id: + rooms[token]['youtube_url'] = url + rooms[token]['youtube_state'] = {'isPlaying': False, 'currentTime': 0, 'last_sync_time': time.time(), 'videoId': video_id} + save_json(ROOMS_DB, rooms) + emit('set_youtube_url', {'videoId': video_id}, room=token) + +@socketio.on('youtube_state_change') +def handle_youtube_state_change(data): + token = data['token'] + if token in rooms: + rooms[token]['youtube_state']['isPlaying'] = (data['action'] == 'play') + rooms[token]['youtube_state']['currentTime'] = data.get('time', 0) + rooms[token]['youtube_state']['last_sync_time'] = time.time() + save_json(ROOMS_DB, rooms) + emit('youtube_state_change', data, room=token, include_self=False) + +@socketio.on('request_youtube_state') +def handle_request_youtube_state(data): + token = data['token'] + if token in rooms and rooms[token]['youtube_url']: + state = rooms[token]['youtube_state'] + elapsed = time.time() - state['last_sync_time'] + estimated_time = state['currentTime'] + elapsed if state['isPlaying'] else state['currentTime'] + emit('youtube_initial_state', { + 'videoId': state.get('videoId'), + 'time': estimated_time, + 'isPlaying': state['isPlaying'] + }, to=request.sid) @socketio.on('start_game') def handle_start_game(data): token = data['token'] game_id = data['game_id'] - - if token in rooms and rooms[token]['admin'] == session.get('username'): + username = session.get('username') + if token in rooms and rooms[token].get('admin') == username: rooms[token]['current_game'] = game_id - games_data[game_id]['state'][token] = {} # Инициализируем состояние игры для этой комнаты + if token not in games_data[game_id]['state']: + games_data[game_id]['state'][token] = {} + games_data[game_id]['state'][token]['players'] = rooms[token]['users'] + rooms[token]['guests'] save_json(ROOMS_DB, rooms) - save_json(GAMES_DB, games_data) # Сохраняем изменения в games_data + save_json(GAMES_DB, games_data) emit('game_started', {'game_id': game_id}, room=token) + emit('update_game_state', {'game_id': game_id, 'state': games_data[game_id]['state'][token]}, room=token) @socketio.on('set_game_state') def handle_set_game_state(data): token = data['token'] game_id = data['game_id'] state = data['state'] - #print("SET STATE", data) - - if token in rooms: - if rooms[token]['admin'] == session.get('username'): - # Проверяем, что игра выбрана и текущий игрок - админ - if rooms[token]['current_game'] == game_id : - games_data[game_id]['state'][token] = state # Сохраняем в games_data - save_json(GAMES_DB, games_data) - emit('update_game_state', {'game_id': game_id, 'state': state}, room=token) - - if (game_id == 'crocodile' or game_id == 'alias') and state.get('isRunning'): # Запуск таймера - start_timer(token, game_id) + username = session.get('username') + if token in rooms and rooms[token].get('admin') == username and rooms[token]['current_game'] == game_id: + games_data[game_id]['state'][token] = state + save_json(GAMES_DB, games_data) + emit('update_game_state', {'game_id': game_id, 'state': state}, room=token) @socketio.on('game_action') def handle_game_action(data): - token = data['token'] - game_id = data['game_id'] - action = data['action'] - value = data.get('value') # Могут быть и другие данные (не только value) - user = data['user'] - card = data.get('card') # Для карт - - if token in rooms and game_id == rooms[token]['current_game']: - current_state = games_data[game_id]['state'].get(token, {}) - - if game_id == 'crocodile' or game_id == 'alias': - if action == 'guess' and current_state.get('isRunning'): - result = "Не угадано" - if value.lower() == current_state.get('word', '').lower(): - result = "Угадано!" - - if 'guesses' not in current_state: - current_state['guesses'] = [] - current_state['guesses'].append({'user':user, 'value': value, 'result':result}) - - games_data[game_id]['state'][token] = current_state - save_json(GAMES_DB, games_data) - emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token) - - elif game_id == 'mafia': - if action == 'vote' and current_state.get('phase') == 'day' and current_state.get('isRunning'): - if 'votes' not in current_state: - current_state['votes'] = {} - - current_state['votes'][user] = value # Сохраняем голос - - #Если все проголосовали - if len(current_state['votes']) == len(rooms[token]['users']): - #Меняем фазу на ночь - current_state['phase'] = 'night' - - games_data[game_id]['state'][token] = current_state - save_json(GAMES_DB, games_data); - emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token) - - elif game_id == 'durak': - if action == 'move': - if current_state.get('attacker') == user: - # Проверяем, можно ли походить этой картой - if len(current_state['table']) == 0 or any(c['rank'] == card['rank'] for pair in current_state['table'] for c in [pair.get('attackingCard'), pair.get('defendingCard')] if c): - current_state['table'].append({'attackingCard': card, 'defendingCard': None}) - current_state['hands'][user].remove(card) - #Передаем ход защищающемуся - current_state['turn'] = 1; - current_state['defender'] = get_next_player(token, current_state['attacker']); - - games_data[game_id]['state'][token] = current_state - save_json(GAMES_DB, games_data) - emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token) - - elif current_state.get('defender') == user: - - if len(current_state['table']) > 0 : - last_pair = current_state['table'][-1] - if last_pair.get('defendingCard') is None: - attacking_card = last_pair['attackingCard'] - #Можем ли побить - if canBeat(attacking_card, card, current_state['trumpSuit']): - last_pair['defendingCard'] = card - current_state['hands'][user].remove(card) - current_state['turn'] = 0 #Ход переходит атакующему - #Меняем атакующего и защищающегося, если нужно - current_state['attacker'] = get_next_player(token, current_state['defender']); - current_state['defender'] = get_next_player(token, current_state['attacker']); - - games_data[game_id]['state'][token] = current_state - save_json(GAMES_DB, games_data) - emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token) - - elif action == 'take': - if current_state.get('defender') == user: - # Защищающийся берет карты со стола - taken_cards = [] - for pair in current_state['table']: - taken_cards.append(pair['attackingCard']) - if pair.get('defendingCard'): - taken_cards.append(pair['defendingCard']) - current_state['hands'][user].extend(taken_cards) - current_state['table'] = [] # Очищаем стол - #Добираем карты - current_state = refill_hands(current_state, token); - - # Ход переходит к следующему игроку после защищавшегося. - current_state['attacker'] = get_next_player(token, current_state['defender']); - current_state['defender'] = get_next_player(token, current_state['attacker']); - current_state['turn'] = 0; # Ходит атакующий - - games_data[game_id]['state'][token] = current_state - save_json(GAMES_DB, games_data) - emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token) - - elif action == 'done': - if current_state.get('attacker') == user: - current_state['table'] = [] - current_state = refill_hands(current_state, token); #Раздача - - #Определение следующего атакующего и защищающегося - current_state['attacker'] = get_next_player(token, current_state['attacker']); - current_state['defender'] = get_next_player(token, current_state['attacker']); - current_state['turn'] = 0; # Ходит атакующий - - games_data[game_id]['state'][token] = current_state; - save_json(GAMES_DB, games_data); - emit('update_game_state', {'game_id': game_id, 'state':current_state}, room=token); - - -def get_next_player(token, current_player): - """Определяет следующего игрока в комнате.""" - if token not in rooms: - return None - - users = rooms[token]['users'] - if not users: - return None - - current_index = users.index(current_player) - next_index = (current_index + 1) % len(users) # Циклический переход - return users[next_index] - -def refill_hands(game_state, token): - """Раздает карты игрокам до 6, если в колоде еще есть карты.""" - players = rooms[token]['users'] - for player in players: - while len(game_state['hands'].get(player, [])) < 6 and len(game_state['deck']) > 0: - game_state['hands'][player].append(game_state['deck'].pop()) - - #Проверка, не закончилась ли игра - if len(game_state['deck']) == 0: - players_without_cards = [player for player in players if len(game_state['hands'].get(player,[])) == 0] - if len(players_without_cards) > 0: - game_state['isGameEnd'] = True - game_state['winner'] = players_without_cards[0] # Первый, кто избавился от карт - - return game_state; - -def start_timer(token, game_id): - if game_id != 'crocodile' and game_id != 'alias': # Таймер пока только для крокодила - return - - def timer_loop(): - with app.app_context(): - while True: - - if token not in rooms or rooms[token]['current_game'] != game_id: - break # Выход, если комната удалена или игра сменилась - - if not games_data[game_id]['state'].get(token) or not games_data[game_id]['state'][token].get('isRunning'): - break #Выход , исли игра не идет - - games_data[game_id]['state'][token]['timer'] -= 1 - save_json(GAMES_DB, games_data) - socketio.emit('timer_tick', {'game_id': game_id, 'time': games_data[game_id]['state'][token]['timer']}, room=token) - - if games_data[game_id]['state'][token]['timer'] <= 0: - games_data[game_id]['state'][token]['isRunning'] = False - save_json(GAMES_DB, games_data) - socketio.emit('update_game_state', {'game_id': game_id, 'state': games_data[game_id]['state'][token]}, room=token) - break # Конец отсчета - - socketio.sleep(1) - - socketio.start_background_task(timer_loop) + token = data['token'] + game_id = data['game_id'] + user = data['user'] + if token in rooms and game_id == rooms[token].get('current_game'): + emit('update_game_state', {'game_id': game_id, 'state': games_data[game_id]['state'][token]}, room=token) if __name__ == '__main__': - # Запускаем фоновый поток для резервного копирования backup_thread = threading.Thread(target=periodic_backup, daemon=True) backup_thread.start() - socketio.run(app, host='0.0.0.0', port=7860, debug=True, allow_unsafe_werkzeug=True) \ No newline at end of file