Add new PNG images for clean and preview outputs
- Added `image_clean.png` to the output directory for the clean image representation. - Added `image_clean_preview.png` for the preview of the clean image. - Introduced `image_svg_clean.png` for the SVG clean image representation.
This commit is contained in:
@@ -9,24 +9,22 @@ import uuid
|
||||
import platform
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
from score_api_client import ScoreAPIClient
|
||||
|
||||
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", api_url="http://172.27.23.245:8000"):
|
||||
self.profiles_file = profiles_file
|
||||
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_client = ScoreAPIClient(api_url)
|
||||
self.api_enabled = self.api_client.is_server_available()
|
||||
self.api_enabled = False
|
||||
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"""
|
||||
@@ -48,17 +46,12 @@ class UserProfileIntegration:
|
||||
def load_active_profile(self):
|
||||
"""Load the currently active profile"""
|
||||
try:
|
||||
with open(self.profiles_file, 'r') as f:
|
||||
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']}")
|
||||
|
||||
# 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}")
|
||||
@@ -80,40 +73,8 @@ class UserProfileIntegration:
|
||||
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
|
||||
return True
|
||||
|
||||
def update_game_stats(self, score, completed=True):
|
||||
"""Update the current profile's game statistics"""
|
||||
@@ -121,42 +82,23 @@ class UserProfileIntegration:
|
||||
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:
|
||||
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]
|
||||
|
||||
# Update statistics
|
||||
if completed:
|
||||
profile['games_played'] += 1
|
||||
print(f"Game completed for {profile_name}! Total games: {profile['games_played']}")
|
||||
|
||||
profile['total_score'] += score
|
||||
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()
|
||||
|
||||
@@ -164,7 +106,7 @@ class UserProfileIntegration:
|
||||
self.current_profile = profile
|
||||
|
||||
# Save back to file
|
||||
with open(self.profiles_file, 'w') as f:
|
||||
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']}")
|
||||
@@ -181,7 +123,7 @@ class UserProfileIntegration:
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(self.profiles_file, 'r') as f:
|
||||
with self.profiles_file.open('r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
profile_name = self.current_profile['name']
|
||||
@@ -192,7 +134,7 @@ class UserProfileIntegration:
|
||||
profile['achievements'].append(achievement_id)
|
||||
self.current_profile = profile
|
||||
|
||||
with open(self.profiles_file, 'w') as f:
|
||||
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}")
|
||||
@@ -220,44 +162,70 @@ class UserProfileIntegration:
|
||||
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
|
||||
return self._get_local_leaderboard(limit)
|
||||
|
||||
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
|
||||
return self._get_local_leaderboard(limit)
|
||||
|
||||
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
|
||||
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 scores from server for a user (defaults to current profile)"""
|
||||
if not self.api_enabled:
|
||||
return []
|
||||
|
||||
"""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']
|
||||
|
||||
scores = self.api_client.get_user_scores(self.device_id, user_id, limit)
|
||||
return scores
|
||||
|
||||
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)"""
|
||||
|
||||
Reference in New Issue
Block a user