wokring marketplace without enchancts and durability on item

This commit is contained in:
2025-07-19 04:13:04 +05:00
parent 44e12723ad
commit 6b8f116608
6 changed files with 351 additions and 1 deletions

243
app/services/marketplace.py Normal file
View File

@ -0,0 +1,243 @@
import uuid
from datetime import datetime
from fastapi import HTTPException
from app.db.database import db
from app.services.coins import CoinsService
from app.services.server.command import CommandService
# Коллекция для хранения товаров на торговой площадке
marketplace_collection = db.marketplace_items
# Добавьте эту функцию
def _serialize_mongodb_doc(doc):
"""Преобразует MongoDB документ для JSON сериализации"""
if doc is None:
return None
result = {}
for key, value in doc.items():
# Обработка ObjectId
if key == "_id":
result["_id"] = str(value)
continue
# Обработка ISODate
if isinstance(value, datetime):
result[key] = value.isoformat()
# Обработка вложенных словарей
elif isinstance(value, dict):
if "$date" in value:
# Это ISODate
result[key] = datetime.fromisoformat(value["$date"].replace("Z", "+00:00")).isoformat()
else:
result[key] = _serialize_mongodb_doc(value)
# Обработка списков
elif isinstance(value, list):
result[key] = [_serialize_mongodb_doc(item) if isinstance(item, dict) else item for item in value]
else:
result[key] = value
return result
class MarketplaceService:
async def list_items(self, server_ip: str = None, page: int = 1, limit: int = 20):
"""Получить список предметов на торговой площадке"""
query = {}
if server_ip:
query["server_ip"] = server_ip
total = await marketplace_collection.count_documents(query)
items_cursor = marketplace_collection.find(query) \
.sort("created_at", -1) \
.skip((page - 1) * limit) \
.limit(limit)
items = await items_cursor.to_list(limit)
# Преобразуем каждый документ
serialized_items = [_serialize_mongodb_doc(item) for item in items]
return {
"items": serialized_items,
"total": total,
"page": page,
"pages": (total + limit - 1) // limit
}
async def get_item(self, item_id: str):
"""Получить информацию о конкретном предмете"""
item = await marketplace_collection.find_one({"id": item_id})
if not item:
raise HTTPException(status_code=404, detail="Предмет не найден")
return _serialize_mongodb_doc(item)
async def add_item(self, username: str, slot_index: int, amount: int, price: int, server_ip: str):
"""Выставить предмет на продажу"""
# 1. Получаем инвентарь игрока
cmd_service = CommandService()
inventory_result = await cmd_service.get_player_inventory(username, server_ip, timeout=15)
if inventory_result["status"] != "success":
raise HTTPException(status_code=400,
detail=f"Не удалось получить инвентарь игрока. Игрок должен быть онлайн.")
# 2. Находим предмет в указанном слоте
item_data = None
for item in inventory_result.get("inventory", []):
if item.get("slot") == slot_index:
item_data = item
break
if not item_data or item_data.get("material") == "AIR" or item_data.get("amount") < amount:
raise HTTPException(status_code=400,
detail=f"В указанном слоте нет предмета или недостаточное количество")
# 3. Создаем запись о предмете на торговой площадке
item_id = str(uuid.uuid4())
marketplace_item = {
"id": item_id,
"material": item_data.get("material"),
"amount": amount,
"price": price,
"seller_name": username,
"server_ip": server_ip,
"display_name": item_data.get("display_name"),
"lore": item_data.get("lore"),
"enchants": item_data.get("enchants"),
"item_data": item_data,
"created_at": datetime.utcnow()
}
await marketplace_collection.insert_one(marketplace_item)
# 4. Удаляем предмет из инвентаря игрока
# Определяем тип слота и его номер для команды replaceitem
slot_type = "inventory"
slot_num = slot_index
# Преобразуем слот инвентаря в соответствующий формат для команды
if 0 <= slot_index <= 8:
# Панель быстрого доступа (хотбар)
slot_type = "hotbar"
slot_num = slot_index
elif 9 <= slot_index <= 35:
# Основной инвентарь
slot_type = "inventory"
slot_num = slot_index - 9
elif slot_index == 36:
# Ботинки
slot_type = "armor.feet"
slot_num = 0
elif slot_index == 37:
# Поножи
slot_type = "armor.legs"
slot_num = 0
elif slot_index == 38:
# Нагрудник
slot_type = "armor.chest"
slot_num = 0
elif slot_index == 39:
# Шлем
slot_type = "armor.head"
slot_num = 0
elif slot_index == 40:
# Вторая рука
slot_type = "weapon.offhand"
slot_num = 0
# Выполняем команду
# Для Minecraft 1.16.5+
command = f"item replace entity {username} {slot_type}.{slot_num} with air"
# Для более старых версий (1.13-1.16)
# command = f"replaceitem entity {username} {slot_type}.{slot_num} air"
from app.models.server.command import ServerCommand
cmd = ServerCommand(
command=command,
server_ip=server_ip,
require_online_player=True,
target_message=f"Вы выставили на продажу {amount} шт. предмета {item_data.get('display_name', item_data.get('material'))} за {price} монет"
)
await cmd_service.add_command(cmd)
return {"status": "success", "item_id": item_id}
async def buy_item(self, buyer_username: str, item_id: str):
"""Купить предмет с торговой площадки"""
# 1. Находим предмет
item = await marketplace_collection.find_one({"id": item_id})
if not item:
raise HTTPException(status_code=404, detail="Предмет не найден")
# 2. Проверяем, что покупатель не является продавцом
if item["seller_name"] == buyer_username:
raise HTTPException(status_code=400, detail="Вы не можете купить свой же предмет")
# 3. Проверяем баланс покупателя
coins_service = CoinsService()
buyer_balance = await coins_service.get_balance(buyer_username)
if buyer_balance < item["price"]:
raise HTTPException(status_code=400,
detail=f"Недостаточно монет. Требуется: {item['price']}, имеется: {buyer_balance}")
# 4. Проверяем, что покупатель онлайн на сервере
cmd_service = CommandService()
try:
await cmd_service.get_player_inventory(buyer_username, item["server_ip"], timeout=5)
except:
raise HTTPException(status_code=400,
detail=f"Вы должны быть онлайн на сервере для совершения покупки")
# 5. Списываем деньги с покупателя
await coins_service.decrease_balance(buyer_username, item["price"])
# 6. Начисляем деньги продавцу
await coins_service.increase_balance(item["seller_name"], item["price"])
# 7. Добавляем предмет в инвентарь покупателя
material = item["material"]
amount = item["amount"]
# Создаем команду с учетом всех свойств предмета
command_base = f"give {buyer_username} {material} {amount}"
# Если у предмета есть мета-данные, добавляем их через NBT
nbt_tags = []
if item.get("display_name"):
nbt_tags.append(f'display:{{Name:\'[{{"text":"{item["display_name"]}","italic":false}}]\'}}')
if item.get("lore"):
lore_json = ','.join([f'[{{"text":"{line}","italic":false}}]' for line in item["lore"]])
nbt_tags.append(f'display:{{Lore:[{lore_json}]}}')
if item.get("enchants"):
enchant_tags = []
for ench_id, level in item["enchants"].items():
enchant_tags.append(f'{{id:"{ench_id}",lvl:{level}s}}')
nbt_tags.append(f'Enchantments:[{",".join(enchant_tags)}]')
if nbt_tags:
command_base += " " + "{" + ",".join(nbt_tags) + "}"
from app.models.server.command import ServerCommand
cmd = ServerCommand(
command=command_base,
server_ip=item["server_ip"],
require_online_player=True,
target_message=f"Вы купили {amount} шт. предмета {item.get('display_name', material)} за {item['price']} монет"
)
await cmd_service.add_command(cmd)
# 8. Удаляем предмет с торговой площадки
await marketplace_collection.delete_one({"id": item_id})
return {
"status": "success",
"message": f"Вы купили {amount} шт. предмета {item.get('display_name', material)}",
"remaining_balance": buyer_balance - item["price"]
}