324 lines
12 KiB
Python
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
|