#!/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 runtime_paths import DEFAULT_PROFILE_DATA, persistent_data_path class UserProfileIntegration: """Integration layer between the game and profile system""" def __init__(self, profiles_file="user_profiles.json"): self.profiles_file = persistent_data_path( profiles_file, default_text=DEFAULT_PROFILE_DATA, ) self.current_profile = None self.device_id = self.generate_device_id() self.api_enabled = False self.load_active_profile() 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 self.profiles_file.open('r', encoding='utf-8') 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']}") 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 register_new_user(self, user_id): return True 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 try: # Update local profile with self.profiles_file.open('r', encoding='utf-8') as f: data = json.load(f) profile_name = self.current_profile['name'] if profile_name in data['profiles']: profile = data['profiles'][profile_name] profile['games_played'] += 1 profile['total_score'] += max(0, score) if score > profile['best_score']: profile['best_score'] = score print(f"New best score for {profile_name}: {score}!") if completed and 'first_win' not in profile['achievements']: profile['achievements'].append('first_win') profile['last_played'] = datetime.now().isoformat() # Update our local copy self.current_profile = profile # Save back to file with self.profiles_file.open('w', encoding='utf-8') 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 self.profiles_file.open('r', encoding='utf-8') 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 self.profiles_file.open('w', encoding='utf-8') 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): return self._get_local_leaderboard(limit) def get_global_leaderboard(self, limit=10): return self._get_local_leaderboard(limit) def get_all_device_users(self): leaderboard = self._get_local_leaderboard(limit=None) return [ { 'user_id': entry['user_id'], 'best_score': entry['best_score'], 'total_games': entry['total_games'], 'device_id': entry['device_id'], } for entry in leaderboard ] def get_user_server_scores(self, user_id=None, limit=10): """Get recent local scores for a user (defaults to current profile).""" if user_id is None: if not self.current_profile: return [] user_id = self.current_profile['name'] table = [] try: score_file_path = persistent_data_path('scores.txt', default_text='') with score_file_path.open(encoding='utf-8') as score_file: for row in score_file.read().splitlines(): parts = row.split(' - ') if len(parts) >= 4 and parts[2] == user_id: table.append({ 'last_play': parts[0], 'score': int(parts[1]), 'user_id': parts[2], 'device_id': parts[3], }) except FileNotFoundError: return [] table.sort(key=lambda entry: entry['score'], reverse=True) return table[:limit] def _get_local_leaderboard(self, limit=10): try: with self.profiles_file.open('r', encoding='utf-8') as profile_file: data = json.load(profile_file) except (FileNotFoundError, json.JSONDecodeError): return [] entries = [] for profile in data.get('profiles', {}).values(): entries.append({ 'user_id': profile.get('name', 'Unknown'), 'best_score': profile.get('best_score', 0), 'total_games': profile.get('games_played', 0), 'device_id': self.device_id, 'last_play': profile.get('last_played', ''), }) entries.sort(key=lambda entry: (entry['best_score'], entry['total_games']), reverse=True) if limit is None: return entries return entries[:limit] 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")