Files
echoes-of-the-ash/web-map/player_endpoints_fixed.py
2025-11-27 16:27:01 +01:00

324 lines
12 KiB
Python

# Player Management API Endpoints - Fixed for accounts+characters schema
@app.route('/api/editor/players', methods=['GET'])
@require_auth
def get_players():
"""Get list of all characters with their account info"""
sys.path.insert(0, str(PARENT_DIR))
try:
from api import database
except ImportError:
return jsonify({'error': 'Database module not available'}), 500
import asyncio
import time
from sqlalchemy import text
async def fetch_players():
players_list = []
try:
async with database.engine.connect() as conn:
# Get all characters with account info
result = await conn.execute(text("""
SELECT c.id, c.account_id, c.name, c.location_id, c.hp, c.max_hp,
c.stamina, c.max_stamina, c.level, c.xp, c.strength, c.agility,
c.endurance, c.intellect, c.unspent_points,
a.email, a.premium_expires_at, a.created_at, c.created_at as character_created_at
FROM characters c
LEFT JOIN accounts a ON c.account_id = a.id
ORDER BY c.name
"""))
rows = result.fetchall()
for row in rows:
players_list.append({
'id': row[0],
'account_id': row[1],
'character_name': row[2],
'location_id': row[3],
'hp': row[4],
'max_hp': row[5],
'stamina': row[6],
'max_stamina': row[7],
'level': row[8],
'xp': row[9],
'strength': row[10],
'agility': row[11],
'endurance': row[12],
'intellect': row[13],
'unspent_points': row[14],
'email': row[15],
'premium_expires_at': row[16],
'account_created_at': row[17],
'character_created_at': row[18],
'is_premium': row[16] and row[16] > time.time() if row[16] else False
})
except Exception as e:
print(f"[PLAYERS] Error fetching players: {e}", flush=True)
import traceback
traceback.print_exc()
return players_list
players = asyncio.run(fetch_players())
return jsonify({'players': players})
@app.route('/api/editor/player/<int:character_id>', methods=['GET'])
@require_auth
def get_player_details(character_id):
"""Get detailed character information including inventory"""
sys.path.insert(0, str(PARENT_DIR))
try:
from api import database
except ImportError:
return jsonify({'error': 'Database module not available'}), 500
import asyncio
from sqlalchemy import text
async def fetch_player_details():
player_data = {}
try:
async with database.engine.connect() as conn:
# Get character basic info with account
result = await conn.execute(text("""
SELECT c.*, a.email, a.premium_expires_at, a.created_at as account_created_at
FROM characters c
LEFT JOIN accounts a ON c.account_id = a.id
WHERE c.id = :cid
"""), {'cid': character_id})
row = result.fetchone()
if not row:
return None
player_data = dict(row._mapping)
# Get inventory
result = await conn.execute(text("""
SELECT i.item_id, i.quantity, i.is_equipped, i.unique_item_id,
u.durability, u.max_durability, u.tier, u.unique_stats
FROM inventory i
LEFT JOIN unique_items u ON i.unique_item_id = u.id
WHERE i.character_id = :cid
"""), {'cid': character_id})
inventory = []
equipped = {}
for row in result.fetchall():
item_data = {
'item_id': row[0],
'quantity': row[1]
}
if row[3]: # Has unique_item_id
item_data['unique_item_data'] = {
'durability': row[4],
'max_durability': row[5],
'tier': row[6],
'unique_stats': row[7]
}
if row[2]: # is_equipped
equipped[row[0]] = item_data
else:
inventory.append(item_data)
player_data['inventory'] = inventory
player_data['equipped'] = equipped
except Exception as e:
print(f"[PLAYER-DETAILS] Error: {e}", flush=True)
import traceback
traceback.print_exc()
return None
return player_data
player = asyncio.run(fetch_player_details())
if not player:
return jsonify({'error': 'Player not found'}), 404
return jsonify(player)
@app.route('/api/editor/player/<int:character_id>', methods=['POST'])
@require_auth
def update_player(character_id):
"""Update character stats and properties"""
sys.path.insert(0, str(PARENT_DIR))
try:
from api import database
except ImportError:
return jsonify({'error': 'Database module not available'}), 500
import asyncio
from sqlalchemy import text
data = request.get_json()
async def update_player_data():
try:
async with database.engine.begin() as conn:
# Update character stats
await conn.execute(text("""
UPDATE characters SET
name = :name,
location_id = :location,
hp = :hp,
max_hp = :max_hp,
stamina = :stamina,
max_stamina = :max_stamina,
level = :level,
xp = :xp,
strength = :str,
agility = :agi,
endurance = :end,
intellect = :int,
unspent_points = :unspent
WHERE id = :cid
"""), {
'cid': character_id,
'name': data.get('character_name'),
'location': data.get('location_id'),
'hp': data.get('hp'),
'max_hp': data.get('max_hp'),
'stamina': data.get('stamina'),
'max_stamina': data.get('max_stamina'),
'level': data.get('level'),
'xp': data.get('xp'),
'str': data.get('strength'),
'agi': data.get('agility'),
'end': data.get('endurance'),
'int': data.get('intellect'),
'unspent': data.get('unspent_points', 0)
})
return True
except Exception as e:
print(f"[UPDATE-PLAYER] Error: {e}", flush=True)
import traceback
traceback.print_exc()
return False
success = asyncio.run(update_player_data())
if success:
return jsonify({'success': True, 'message': 'Player updated successfully'})
else:
return jsonify({'error': 'Failed to update player'}), 500
# Simplified inventory/equipment endpoints - not fully implemented
@app.route('/api/editor/player/<int:character_id>/inventory', methods=['POST'])
@require_auth
def update_player_inventory(character_id):
return jsonify({'success': True, 'message': 'Inventory editing not yet implemented'})
@app.route('/api/editor/player/<int:character_id>/equipment', methods=['POST'])
@require_auth
def update_player_equipment(character_id):
return jsonify({'success': True, 'message': 'Equipment managed via inventory'})
# Simplified account management - accounts don't have ban functionality in new schema
@app.route('/api/editor/account/<int:account_id>/ban', methods=['POST'])
@require_auth
def ban_account(account_id):
return jsonify({'success': True, 'message': 'Ban functionality not implemented in new schema'})
@app.route('/api/editor/account/<int:account_id>/delete', methods=['DELETE'])
@require_auth
def delete_account(account_id):
"""Delete an account and all associated characters"""
sys.path.insert(0, str(PARENT_DIR))
try:
from api import database
except ImportError:
return jsonify({'error': 'Database module not available'}), 500
import asyncio
from sqlalchemy import text
async def delete_account_data():
try:
async with database.engine.begin() as conn:
# CASCADE will handle characters and their inventory
await conn.execute(text("DELETE FROM accounts WHERE id = :aid"), {'aid': account_id})
return True
except Exception as e:
print(f"[DELETE-ACCOUNT] Error: {e}", flush=True)
import traceback
traceback.print_exc()
return False
success = asyncio.run(delete_account_data())
if success:
return jsonify({'success': True, 'message': 'Account deleted successfully'})
else:
return jsonify({'error': 'Failed to delete account'}), 500
@app.route('/api/editor/player/<int:character_id>/reset', methods=['POST'])
@require_auth
def reset_player(character_id):
"""Reset character to starting state"""
sys.path.insert(0, str(PARENT_DIR))
try:
from api import database
except ImportError:
return jsonify({'error': 'Database module not available'}), 500
import asyncio
from sqlalchemy import text
async def reset_player_data():
try:
async with database.engine.begin() as conn:
# Clear inventory
await conn.execute(text("DELETE FROM inventory WHERE character_id = :cid"), {'cid': character_id})
# Reset character stats to defaults
await conn.execute(text("""
UPDATE characters SET
location_id = 'cabin',
hp = 100,
max_hp = 100,
stamina = 100,
max_stamina = 100,
level = 1,
xp = 0,
strength = 0,
agility = 0,
endurance = 0,
intellect = 0,
unspent_points = 20,
is_dead = false
WHERE id = :cid
"""), {'cid': character_id})
return True
except Exception as e:
print(f"[RESET-PLAYER] Error: {e}", flush=True)
import traceback
traceback.print_exc()
return False
success = asyncio.run(reset_player_data())
if success:
return jsonify({'success': True, 'message': 'Player reset successfully'})
else:
return jsonify({'error': 'Failed to reset player'}), 500