#!/usr/bin/env python3 """ User Profile Integration Module Provides integration between games and the user profile system """ import json import uuid import platform import hashlib from datetime import datetime from engine.score_api_client import ScoreAPIClient class UserProfileIntegration: """Integration layer between the game and profile system""" def __init__(self, profiles_file="user_profiles.json", api_url="http://localhost:8000"): self.profiles_file = profiles_file self.current_profile = None self.device_id = self.generate_device_id() self.api_client = ScoreAPIClient(api_url) self.api_enabled = self.api_client.is_server_available() self.load_active_profile() if self.api_enabled: print(f"✓ Connected to score server at {api_url}") else: print(f"✗ Score server not available at {api_url} - running offline") def generate_device_id(self): """Generate a unique device ID based on system information""" # Get system information system_info = f"{platform.system()}-{platform.machine()}-{platform.processor()}" # Try to get MAC address for more uniqueness try: mac = ':'.join(['{:02x}'.format((uuid.getnode() >> ele) & 0xff) for ele in range(0,8*6,8)][::-1]) system_info += f"-{mac}" except: pass # Create hash and take first 8 characters device_hash = hashlib.md5(system_info.encode()).hexdigest()[:8].upper() return f"DEV-{device_hash}" def load_active_profile(self): """Load the currently active profile""" try: with open(self.profiles_file, 'r') as f: data = json.load(f) active_name = data.get('active_profile') if active_name and active_name in data['profiles']: self.current_profile = data['profiles'][active_name] print(f"Loaded profile: {self.current_profile['name']}") # Sync with API if available if self.api_enabled: self.sync_profile_with_api() return True except (FileNotFoundError, json.JSONDecodeError) as e: print(f"Could not load profile: {e}") self.current_profile = None return False def get_profile_name(self): """Get current profile name or default""" return self.current_profile['name'] if self.current_profile else "Guest Player" def get_device_id(self): """Get the unique device identifier""" return self.device_id def get_setting(self, setting_name, default_value=None): """Get a setting from the current profile, or return default""" if self.current_profile and 'settings' in self.current_profile: return self.current_profile['settings'].get(setting_name, default_value) return default_value def sync_profile_with_api(self): """Ensure current profile is registered with the API server""" if not self.current_profile or not self.api_enabled: return False profile_name = self.current_profile['name'] # Check if user exists on server if not self.api_client.user_exists(self.device_id, profile_name): print(f"Registering {profile_name} with score server...") result = self.api_client.signup_user(self.device_id, profile_name) if result.get('success'): print(f"✓ {profile_name} registered successfully") return True else: print(f"✗ Failed to register {profile_name}: {result.get('message')}") return False else: print(f"✓ {profile_name} already registered on server") return True def register_new_user(self, user_id): """Register a new user both locally and on the API server""" if not self.api_enabled: print("API server not available - user will only be registered locally") return True result = self.api_client.signup_user(self.device_id, user_id) if result.get('success'): print(f"✓ {user_id} registered with server successfully") return True else: print(f"✗ Failed to register {user_id} with server: {result.get('message')}") return False def update_game_stats(self, score, completed=True): """Update the current profile's game statistics""" if not self.current_profile: print("No profile loaded - stats not saved") return False # Submit score to API first if available if self.api_enabled: profile_name = self.current_profile['name'] result = self.api_client.submit_score( self.device_id, profile_name, score, completed ) if result.get('success'): print(f"✓ Score {score} submitted to server successfully") # Print server stats if available if 'user_stats' in result: stats = result['user_stats'] print(f" Server stats - Games: {stats['total_games']}, Best: {stats['best_score']}") else: print(f"✗ Failed to submit score to server: {result.get('message')}") try: # Update local profile with open(self.profiles_file, 'r') as f: data = json.load(f) profile_name = self.current_profile['name'] if profile_name in data['profiles']: profile = data['profiles'][profile_name] # Update statistics if completed: profile['games_played'] += 1 print(f"Game completed for {profile_name}! Total games: {profile['games_played']}") profile['total_score'] += score if score > profile['best_score']: profile['best_score'] = score print(f"New best score for {profile_name}: {score}!") profile['last_played'] = datetime.now().isoformat() # Update our local copy self.current_profile = profile # Save back to file with open(self.profiles_file, 'w') as f: json.dump(data, f, indent=2) print(f"Local profile stats updated: Score +{score}, Total: {profile['total_score']}") return True except Exception as e: print(f"Error updating profile stats: {e}") return False def add_achievement(self, achievement_id): """Add an achievement to the current profile""" if not self.current_profile: return False try: with open(self.profiles_file, 'r') as f: data = json.load(f) profile_name = self.current_profile['name'] if profile_name in data['profiles']: profile = data['profiles'][profile_name] if achievement_id not in profile['achievements']: profile['achievements'].append(achievement_id) self.current_profile = profile with open(self.profiles_file, 'w') as f: json.dump(data, f, indent=2) print(f"Achievement unlocked for {profile_name}: {achievement_id}") return True except Exception as e: print(f"Error adding achievement: {e}") return False def get_profile_info(self): """Get current profile information for display""" if self.current_profile: info = { 'name': self.current_profile['name'], 'games_played': self.current_profile['games_played'], 'best_score': self.current_profile['best_score'], 'total_score': self.current_profile['total_score'], 'achievements': len(self.current_profile['achievements']), 'difficulty': self.current_profile['settings'].get('difficulty', 'normal'), 'device_id': self.device_id, 'api_connected': self.api_enabled } return info return None def get_device_leaderboard(self, limit=10): """Get leaderboard for the current device from API server""" if not self.api_enabled: print("API server not available - cannot get leaderboard") return [] leaderboard = self.api_client.get_leaderboard(self.device_id, limit) return leaderboard def get_global_leaderboard(self, limit=10): """Get global leaderboard across all devices from API server""" if not self.api_enabled: print("API server not available - cannot get global leaderboard") return [] leaderboard = self.api_client.get_global_leaderboard(limit) return leaderboard def get_all_device_users(self): """Get all users registered for this device from API server""" if not self.api_enabled: print("API server not available - cannot get user list") return [] users = self.api_client.get_device_users(self.device_id) return users def get_user_server_scores(self, user_id=None, limit=10): """Get recent scores from server for a user (defaults to current profile)""" if not self.api_enabled: return [] if user_id is None: if not self.current_profile: return [] user_id = self.current_profile['name'] scores = self.api_client.get_user_scores(self.device_id, user_id, limit) return scores def reload_profile(self): """Reload the current profile from disk (useful for external profile changes)""" return self.load_active_profile() # Convenience functions for quick integration def get_active_profile(): """Quick function to get active profile info""" integration = UserProfileIntegration() return integration.get_profile_info() def update_profile_score(score, completed=True): """Quick function to update profile score""" integration = UserProfileIntegration() return integration.update_game_stats(score, completed) def get_profile_setting(setting_name, default_value=None): """Quick function to get a profile setting""" integration = UserProfileIntegration() return integration.get_setting(setting_name, default_value) def get_device_leaderboard(limit=10): """Quick function to get device leaderboard""" integration = UserProfileIntegration() return integration.get_device_leaderboard(limit) def get_global_leaderboard(limit=10): """Quick function to get global leaderboard""" integration = UserProfileIntegration() return integration.get_global_leaderboard(limit) if __name__ == "__main__": # Test the integration print("Testing User Profile Integration with API...") integration = UserProfileIntegration() print(f"Device ID: {integration.get_device_id()}") print(f"Profile Name: {integration.get_profile_name()}") print(f"API Connected: {integration.api_enabled}") info = integration.get_profile_info() if info: print(f"Profile Info: {info}") else: print("No profile loaded") # Test settings difficulty = integration.get_setting('difficulty', 'normal') sound_volume = integration.get_setting('sound_volume', 50) print(f"Settings - Difficulty: {difficulty}, Sound: {sound_volume}%") # Test API features if connected if integration.api_enabled: print("\nTesting API features...") # Get leaderboard leaderboard = integration.get_device_leaderboard(5) if leaderboard: print("Device Leaderboard:") for entry in leaderboard: print(f" {entry['rank']}. {entry['user_id']}: {entry['best_score']} pts ({entry['total_games']} games)") else: print("No leaderboard data available") # Get all users users = integration.get_all_device_users() print(f"\nTotal users on device: {len(users)}") for user in users: print(f" {user['user_id']}: Best {user['best_score']}, {user['total_scores']} games") # Test score submission if integration.current_profile: print(f"\nTesting score submission for {integration.current_profile['name']}...") result = integration.update_game_stats(1234, True) print(f"Score update result: {result}") else: print("API features not available - server offline")