Files
popa_minecraft_launcher_api/app/services/server/command.py

235 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 # Не создаем новый сервер, только обновляем существующий
)