import uuid from datetime import datetime from fastapi import HTTPException from typing import Dict from app.db.database import db import asyncio # Создаем коллекции для хранения команд и инвентаря pending_commands_collection = db.pending_commands inventory_requests_collection = db.inventory_requests inventory_collection = db.inventory game_servers_collection = db.game_servers class CommandService: async def add_command(self, command_data): try: command_id = str(uuid.uuid4()) command_doc = { "id": command_id, "command": command_data.command, "server_ip": command_data.server_ip, "require_online_player": command_data.require_online_player, "target_message": command_data.target_message if hasattr(command_data, 'target_message') else None, "global_message": command_data.global_message if hasattr(command_data, 'global_message') else None, "created_at": datetime.utcnow() } await pending_commands_collection.insert_one(command_doc) print(f"[{datetime.now()}] Добавлена команда: {command_data.command} " f"для сервера {command_data.server_ip}") # Обновляем last_activity для сервера await self._update_server_activity(command_data.server_ip) return {"status": "success", "command_id": command_id} except Exception as e: raise HTTPException(status_code=400, detail=str(e)) async def get_commands(self, server_ip: str): try: # Получаем команды для указанного сервера commands_cursor = pending_commands_collection.find({"server_ip": server_ip}) commands = await commands_cursor.to_list(1000) result_commands = [ { "id": cmd["id"], "command": cmd["command"], "require_online_player": cmd["require_online_player"], "target_message": cmd.get("target_message"), "global_message": cmd.get("global_message") } for cmd in commands ] # Удаляем полученные команды (чтобы не выполнять их повторно) await pending_commands_collection.delete_many({"server_ip": server_ip}) return {"status": "success", "commands": result_commands} except Exception as e: raise HTTPException(status_code=400, detail=str(e)) async def request_inventory(self, inventory_request): """Создаёт запрос на получение инвентаря игрока""" try: request_id = str(uuid.uuid4()) inventory_request_doc = { "id": request_id, "server_ip": inventory_request.server_ip, "player_name": inventory_request.player_name, "created_at": datetime.utcnow(), "status": "pending" } await inventory_requests_collection.insert_one(inventory_request_doc) print(f"[{datetime.now()}] Запрос инвентаря игрока {inventory_request.player_name} " f"с сервера {inventory_request.server_ip}") # Обновляем last_activity для сервера await self._update_server_activity(inventory_request.server_ip) return {"status": "pending", "request_id": request_id} except Exception as e: raise HTTPException(status_code=400, detail=str(e)) async def get_inventory_requests(self, server_ip: str): """Получает запросы на инвентарь для указанного сервера""" try: requests_cursor = inventory_requests_collection.find( {"server_ip": server_ip, "status": "pending"} ) requests = await requests_cursor.to_list(1000) result_requests = [ { "id": req["id"], "player_name": req["player_name"] } for req in requests ] # Помечаем запросы как обработанные for req in result_requests: await inventory_requests_collection.update_one( {"id": req["id"]}, {"$set": {"status": "processing"}} ) return {"status": "success", "inventory_requests": result_requests} except Exception as e: raise HTTPException(status_code=400, detail=str(e)) async def submit_inventory(self, inventory_data: dict): """Принимает данные инвентаря от сервера""" try: request_id = inventory_data.get("request_id") request = await inventory_requests_collection.find_one({"id": request_id}) if not request: raise HTTPException(status_code=404, detail="Запрос не найден") player_name = request["player_name"] server_ip = request["server_ip"] # Обновляем или создаем запись инвентаря await inventory_collection.update_one( { "player_name": player_name, "server_ip": server_ip }, { "$set": { "inventory_data": inventory_data.get("inventory", []), "updated_at": datetime.utcnow() } }, upsert=True # Создает новую запись, если не найдена существующая ) # Помечаем запрос как выполненный await inventory_requests_collection.update_one( {"id": request_id}, {"$set": {"status": "completed"}} ) return {"status": "success"} except Exception as e: raise HTTPException(status_code=400, detail=str(e)) async def get_inventory_result(self, request_id: str): """Получает результаты запроса инвентаря""" request = await inventory_requests_collection.find_one({"id": request_id}) if not request: raise HTTPException(status_code=404, detail="Запрос не найден") if request["status"] != "completed": return {"status": request["status"]} # Получаем инвентарь из коллекции inventory inventory = await inventory_collection.find_one({ "player_name": request["player_name"], "server_ip": request["server_ip"] }) if not inventory: raise HTTPException(status_code=404, detail="Инвентарь не найден") return { "status": "completed", "result": { "player_name": inventory["player_name"], "server_ip": inventory["server_ip"], "inventory_data": inventory["inventory_data"], "updated_at": inventory["updated_at"] } } async def get_player_inventory(self, player_name: str, server_ip: str, timeout: int = 10): """Запрашивает и ждет получения инвентаря игрока""" try: # Проверяем, есть ли уже актуальный инвентарь existing_inventory = await inventory_collection.find_one({ "player_name": player_name, "server_ip": server_ip }) # Если инвентарь уже есть и он достаточно свежий (не старше 1 минуты) if existing_inventory and "updated_at" in existing_inventory: if (datetime.utcnow() - existing_inventory["updated_at"]).total_seconds() < 60: return { "status": "success", "player_name": existing_inventory["player_name"], "server_ip": existing_inventory["server_ip"], "inventory": existing_inventory["inventory_data"], "updated_at": existing_inventory["updated_at"] } # Запрашиваем новый инвентарь request_id = str(uuid.uuid4()) inventory_request_doc = { "id": request_id, "server_ip": server_ip, "player_name": player_name, "created_at": datetime.utcnow(), "status": "pending" } await inventory_requests_collection.insert_one(inventory_request_doc) print(f"[{datetime.now()}] Запрос инвентаря игрока {player_name} " f"с сервера {server_ip}") # Обновляем last_activity для сервера await self._update_server_activity(server_ip) # Ждем ответа от сервера start_time = datetime.utcnow() while (datetime.utcnow() - start_time).total_seconds() < timeout: result = await self.get_inventory_result(request_id) if result["status"] == "completed": return result await asyncio.sleep(1) # Ждем 1 секунду перед следующей проверкой raise HTTPException(status_code=504, detail="Timeout waiting for inventory") except Exception as e: raise HTTPException(status_code=400, detail=str(e)) async def _update_server_activity(self, server_ip): """Обновляет время последней активности для сервера""" await game_servers_collection.update_one( {"ip": server_ip}, {"$set": {"last_activity": datetime.utcnow()}}, upsert=False # Не создаем новый сервер, только обновляем существующий )