From 44e12723ad0ae319542d57b504c2790c75c1209f Mon Sep 17 00:00:00 2001 From: DIKER0K Date: Sat, 19 Jul 2025 01:20:26 +0500 Subject: [PATCH] add: getting player inventory --- app/api/server.py | 22 +++- app/models/server/command.py | 4 + app/services/server/command.py | 199 ++++++++++++++++++++++++++++++--- 3 files changed, 209 insertions(+), 16 deletions(-) diff --git a/app/api/server.py b/app/api/server.py index ac7dfc9..65f8707 100644 --- a/app/api/server.py +++ b/app/api/server.py @@ -1,7 +1,7 @@ from fastapi import APIRouter from app.services.server.command import CommandService from app.services.server.event import EventService -from app.models.server.command import ServerCommand +from app.models.server.command import ServerCommand, InventoryRequest from datetime import datetime import uuid @@ -28,6 +28,26 @@ async def add_server_command(command_data: ServerCommand): async def get_server_commands(server_ip: str): return await CommandService().get_commands(server_ip) +@router.post("/inventory") +async def request_player_inventory(inventory_request: InventoryRequest): + """Создаёт запрос на получение инвентаря игрока""" + return await CommandService().request_inventory(inventory_request) + +@router.get("/inventory/requests") +async def get_inventory_requests(server_ip: str): + """Получает список запросов инвентаря для сервера""" + return await CommandService().get_inventory_requests(server_ip) + +@router.post("/inventory/submit") +async def submit_inventory(inventory_data: dict): + """Принимает данные инвентаря от сервера""" + return await CommandService().submit_inventory(inventory_data) + +@router.get("/inventory/{request_id}") +async def get_inventory_result(request_id: str): + """Получает результаты запроса инвентаря""" + return await CommandService().get_inventory_result(request_id) + async def update_server_activity(server_ip): """Обновляет время последней активности сервера""" from app.db.database import db diff --git a/app/models/server/command.py b/app/models/server/command.py index 39170a5..d9c541e 100644 --- a/app/models/server/command.py +++ b/app/models/server/command.py @@ -7,3 +7,7 @@ class ServerCommand(BaseModel): require_online_player: Optional[bool] = False target_message: Optional[str] = None # Сообщение для цели global_message: Optional[str] = None # Сообщение для остальных + +class InventoryRequest(BaseModel): + server_ip: str + player_name: str diff --git a/app/services/server/command.py b/app/services/server/command.py index 26b69b4..a4aca99 100644 --- a/app/services/server/command.py +++ b/app/services/server/command.py @@ -2,22 +2,29 @@ import uuid from datetime import datetime from fastapi import HTTPException from typing import Dict +from app.db.database import db -# Глобальное хранилище команд (в реальном проекте используйте БД) -pending_commands: Dict[str, Dict] = {} +# Создаем коллекции для хранения команд и инвентаря +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()) - pending_commands[command_id] = { + 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.now().isoformat() + "created_at": datetime.utcnow() } + + await pending_commands_collection.insert_one(command_doc) print(f"[{datetime.now()}] Добавлена команда: {command_data.command} " f"для сервера {command_data.server_ip}") @@ -31,32 +38,194 @@ class CommandService: async def get_commands(self, server_ip: str): try: # Получаем команды для указанного сервера - commands = [ + commands_cursor = pending_commands_collection.find({"server_ip": server_ip}) + commands = await commands_cursor.to_list(1000) + + result_commands = [ { - "id": cmd_id, + "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_id, cmd in pending_commands.items() - if cmd["server_ip"] == server_ip + for cmd in commands ] # Удаляем полученные команды (чтобы не выполнять их повторно) - for cmd_id in list(pending_commands.keys()): - if pending_commands[cmd_id]["server_ip"] == server_ip: - del pending_commands[cmd_id] + await pending_commands_collection.delete_many({"server_ip": server_ip}) - return {"status": "success", "commands": commands} + 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): """Обновляет время последней активности для сервера""" - from app.db.database import db - game_servers_collection = db.game_servers - await game_servers_collection.update_one( {"ip": server_ip}, {"$set": {"last_activity": datetime.utcnow()}},