20 KiB
EU-Utility Improvement Plan
Comprehensive Code Review & Analysis
Date: February 14, 2026
Analyst: AI Code Review System
Scope: Full codebase analysis of EU-Utility v2.0
1. Executive Summary
Top 10 Critical Issues
| # | Issue | Severity | Impact | File(s) |
|---|---|---|---|---|
| 1 | No Input Validation on File Operations | Critical | Data corruption, security | plugin_manager.py, settings.py, multiple plugins |
| 2 | Race Conditions in Event Bus | Critical | Data loss, crashes | event_bus.py |
| 3 | Missing Exception Handling in OCR Thread | Critical | App crash | game_reader/plugin.py |
| 4 | Hardcoded Paths Without Validation | High | Cross-platform failures | log_reader.py, settings.py |
| 5 | No Rate Limiting on Nexus API | High | API bans, poor UX | nexus_api.py |
| 6 | Memory Leaks in Plugin UI | High | Performance degradation | overlay_window.py, plugins |
| 7 | Unvalidated JSON Parsing | High | Crashes on corrupt data | All plugins with data files |
| 8 | No Cleanup of QThreads | Medium | Resource leaks | skill_scanner/plugin.py, game_reader/plugin.py |
| 9 | Missing Type Hints | Medium | Maintenance burden | Entire codebase |
| 10 | No Logging Framework | Medium | Debugging difficulties | Entire codebase |
Quick Stats
- Total Files Analyzed: 50+
- Core Modules: 15
- Plugins: 26
- Lines of Code: ~15,000+
- Critical Issues: 4
- High Priority Issues: 12
- Medium Priority Issues: 28
2. Architecture Review
2.1 Strengths
-
Well-Structured Plugin System
- Clean
BasePluginabstract class - Proper lifecycle management (initialize, get_ui, shutdown)
- Good separation of concerns between core and plugins
- Clean
-
Comprehensive Service Layer
- OCR Service with multiple backend support
- HTTP Client with caching and retry logic
- Event Bus with typed events
- Task Manager for background operations
-
Cross-Platform Considerations
- Windows-specific features gracefully degrade on Linux
- Platform detection in
window_manager.py - Fallback mechanisms for OCR backends
-
Modern Python Patterns
- Dataclasses for data structures
- Type hints (partial)
- Singleton pattern for services
- Context managers where appropriate
2.2 Weaknesses
-
Tight Coupling to PyQt6
- No abstraction layer for UI framework
- Difficult to test without GUI
- Blocks headless operation
-
Global State Management
- Heavy reliance on singletons
- Difficult to mock for testing
- Potential for circular imports
-
No Dependency Injection
- Services directly instantiate dependencies
- Hard to swap implementations
- Testing requires monkey-patching
-
Inconsistent Error Handling
- Mix of try/except, error returns, and exceptions
- No unified error response format
- Silent failures in many places
-
Missing Abstraction Layers
- Direct file system access everywhere
- No repository pattern for data access
- No configuration abstraction
3. Performance Analysis
3.1 Identified Bottlenecks
| Component | Issue | Impact | Recommendation |
|---|---|---|---|
| OCR Service | Synchronous initialization blocks UI | High startup delay | Move to background thread |
| Event Bus | Lock contention on high-frequency events | Reduced throughput | Use lock-free queues |
| HTTP Client | Blocking I/O in main thread | UI freezing | Use async/await |
| Plugin Manager | Synchronous plugin discovery | Slow startup | Lazy loading |
| Log Reader | 500ms polling interval | CPU waste | Use file system events |
| Screenshot | Full-screen capture on every OCR | Memory pressure | Region-based capture |
3.2 Memory Usage Issues
-
Unbounded Caches
EventBushistory limited to 1000 events (good)HTTPClientcache has no size limit (bad)- Plugin data files loaded entirely into memory (bad)
-
UI Widget Leaks
# In overlay_window.py - widgets not properly deleted while self.plugin_stack.count() > 0: widget = self.plugin_stack.widget(0) self.plugin_stack.removeWidget(widget) # Not deleted! -
Image Retention
- Screenshots kept in memory indefinitely
- No LRU cache for OCR images
3.3 Optimization Recommendations
-
Implement Async Operations
# Current (blocking) result = api.nexus_search(query) # Recommended (async) result = await api.nexus_search_async(query) -
Add Connection Pooling
- HTTP client should reuse connections
- Current implementation creates new session per request
-
Lazy Loading for Plugins
- Only load plugin UI when first viewed
- Current: All UIs created at startup
-
Image Compression
- Compress screenshots before OCR
- Use lower resolution for text detection
4. Code Quality Issues
4.1 Error Handling Deficiencies
Issue: Bare Except Clauses
Files: Multiple plugins
# BAD - Catches everything including KeyboardInterrupt
try:
data = json.load(f)
except:
pass
# GOOD - Specific exception handling
try:
data = json.load(f)
except json.JSONDecodeError as e:
logger.error(f"Invalid JSON in {file_path}: {e}")
data = {}
except IOError as e:
logger.error(f"Failed to read {file_path}: {e}")
raise DataLoadError from e
Issue: Silent Failures
File: core/plugin_manager.py:52-55
# Current
try:
return json.loads(config_path.read_text())
except json.JSONDecodeError:
pass # Silent failure!
# Recommended
try:
return json.loads(config_path.read_text())
except json.JSONDecodeError as e:
logger.warning(f"Corrupted config file, using defaults: {e}")
self._backup_corrupted_config(config_path)
return self._get_default_config()
4.2 Type Safety Issues
Missing Type Hints
Coverage: ~40% of functions lack proper type hints
Priority files to annotate:
core/plugin_api.py- Public API surfacecore/event_bus.py- Critical for event handlingplugins/base_plugin.py- Base class for all plugins
Optional/None Confusion
File: core/ocr_service.py:45-50
# Current - unclear return type
def capture_screen(self, region=None):
# Returns Image.Image or raises
pass
# Recommended
def capture_screen(self, region: Optional[Tuple[int, int, int, int]] = None) -> Image.Image:
"""Capture screen or region.
Args:
region: (x, y, width, height) or None for full screen
Returns:
PIL Image
Raises:
RuntimeError: If screenshot service unavailable
"""
4.3 Code Duplication
Pattern: File Loading
Found in: 12+ plugins
# Duplicated pattern
data_file = Path("data/something.json")
data_file.parent.mkdir(parents=True, exist_ok=True)
if data_file.exists():
try:
with open(data_file, 'r') as f:
data = json.load(f)
except:
data = {}
Solution: Create DataStore utility class
Pattern: UI Styling
Found in: All plugins
Each plugin defines its own styles instead of using shared styles from eu_styles.py.
4.4 Specific File Issues
core/main.py
- Line 45-50: No graceful degradation if keyboard library fails
- Line 120-150: Service initialization not parallelized
- Line 200-220: Missing cleanup on startup failure
core/plugin_manager.py
- Line 85-95: Plugin discovery can hang on malformed plugins
- Line 120-140: No timeout on plugin initialization
- Line 180-200: Hotkey triggering not thread-safe
plugins/game_reader/plugin.py
- Line 45-80: OCR thread has no cancellation mechanism
- Line 100-120: Screenshot temp file not cleaned up on crash
- Line 150-180: No validation of OCR result before processing
5. Security Audit
5.1 Critical Vulnerabilities
1. Path Traversal in Plugin Loading
File: core/plugin_manager.py:85-95
# VULNERABLE - No path validation
spec = importlib.util.spec_from_file_location(module_name, plugin_file)
Risk: Malicious plugin could access files outside plugin directory
Fix:
from pathlib import Path
def _validate_plugin_path(self, plugin_file: Path) -> bool:
"""Ensure plugin file is within allowed directories."""
resolved = plugin_file.resolve()
allowed_paths = [Path(d).resolve() for d in self.PLUGIN_DIRS]
return any(str(resolved).startswith(str(allowed)) for allowed in allowed_paths)
2. Code Injection via Log Parsing
File: core/log_reader.py:85-95
# VULNERABLE - Regex patterns could be exploited
PATTERNS = {
'skill_gain': re.compile(r'(.+?)\s+has\s+improved...'),
}
Risk: Crafted log lines could cause ReDoS (Regular Expression Denial of Service)
Fix: Add regex timeout and complexity limits
3. Unsafe Deserialization
File: Multiple plugins
# VULNERABLE - Loading untrusted JSON
data = json.load(f) # Could contain malicious data
Risk: While json.load is safer than pickle, nested structures could cause memory exhaustion
Fix: Implement depth and size limits
5.2 Medium Risk Issues
1. No HTTPS Certificate Validation
File: core/nexus_api.py:180-200
The requests library validates by default, but no pinning is implemented.
2. Sensitive Data in Logs
File: core/plugin_api.py
API keys or tokens could be logged in error messages.
3. Temp File Race Condition
File: plugins/game_reader/plugin.py:60-70
# VULNERABLE
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
screenshot_path = tmp.name # Predictable path
5.3 Security Recommendations
-
Implement Plugin Signing
- Verify plugin integrity before loading
- Whitelist approved plugins
-
Add Sandboxing
- Restrict plugin file system access
- Network access controls
-
Input Sanitization
- Validate all external inputs
- Escape log message content
-
Secure Defaults
- Disable plugins by default
- Require explicit user approval
6. UI/UX Improvements
6.1 Current Pain Points
-
No Loading States
- OCR operations block UI without feedback
- Network requests show no progress
-
Poor Error Messages
- Generic "Error" messages
- No actionable guidance
-
Inconsistent Styling
- Each plugin has different button styles
- No unified color scheme enforcement
-
No Keyboard Navigation
- Tab order not defined
- No shortcuts for common actions
6.2 Design Recommendations
Implement Toast Notifications
# Add to core/notifications.py
class NotificationManager:
def show_toast(self, message: str, type: str = "info", duration: int = 3000):
"""Show non-intrusive notification."""
Add Loading Indicators
# Add to base_plugin.py
class BasePlugin:
def show_loading(self, message: str = "Loading..."):
"""Show loading overlay."""
def hide_loading(self):
"""Hide loading overlay."""
Standardize Form Validation
# Add validation utilities
class FormValidator:
@staticmethod
def validate_number(value: str, min: float, max: float) -> ValidationResult:
"""Validate numeric input."""
6.3 Accessibility Improvements
-
Add Screen Reader Support
- Set proper
accessibleNameon all widgets - Provide text alternatives for icons
- Set proper
-
High Contrast Mode
- Add theme option for visibility
- Respect system accessibility settings
-
Font Scaling
- Support system font size changes
- Allow user-defined scaling
7. Feature Gaps
7.1 Missing vs Competitors
| Feature | EU-Utility | Competitor A | Competitor B | Priority |
|---|---|---|---|---|
| Auto-loot tracking | Manual only | ✓ Auto | ✓ Auto | P0 |
| Market price alerts | ✗ | ✓ | ✓ | P1 |
| Team/Shared tracking | ✗ | ✓ | ✗ | P2 |
| Mobile companion | ✗ | ✓ | ✓ | P2 |
| Cloud sync | ✗ | ✓ | ✗ | P1 |
| Plugin marketplace | Basic | ✓ | ✗ | P2 |
| Analytics dashboard | Basic | ✓ | ✓ | P1 |
| Discord integration | ✗ | ✓ | ✓ | P3 |
7.2 Technical Debt Features
-
Configuration Management
- Current: JSON files scattered across codebase
- Needed: Unified config with validation schema
-
Data Migration
- No versioning for data files
- Breaking changes lose user data
-
Plugin API Versioning
- No version compatibility checking
- Plugins can break on core updates
-
Update System
- No auto-update mechanism
- Manual download required
7.3 Recommended New Features
P0 (Critical)
-
Automatic Loot Detection
- Parse chat log for loot messages
- Auto-populate loot tracker
-
Session Persistence
- Save/restore hunting sessions
- Crash recovery
P1 (High)
-
Market Price Monitoring
- Track item prices over time
- Alert on price changes
-
Cloud Backup
- Optional encrypted backup
- Cross-device sync
P2 (Medium)
-
Plugin SDK
- Better documentation
- Example plugins
- Debug tools
-
Mobile App
- View stats remotely
- Push notifications
8. Prioritized Action Plan
P0 - Fix Immediately (Critical)
8.1.1 Fix Race Conditions in Event Bus
File: core/event_bus.py
Effort: 4 hours
Impact: Prevents data loss and crashes
# Add proper locking
from threading import RLock
class EventBus:
def __init__(self):
self._lock = RLock()
self._history_lock = RLock()
8.1.2 Add Input Validation to File Operations
Files: All plugins with file I/O Effort: 8 hours Impact: Prevents data corruption
def safe_json_load(filepath: Path, default: Any = None) -> Any:
"""Safely load JSON with validation."""
if not filepath.exists():
return default
# Check file size (prevent memory exhaustion)
max_size = 10 * 1024 * 1024 # 10MB
if filepath.stat().st_size > max_size:
raise FileTooLargeError(f"{filepath} exceeds max size")
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# Validate JSON structure
return json.loads(content)
8.1.3 Fix OCR Thread Exception Handling
File: plugins/game_reader/plugin.py
Effort: 2 hours
Impact: Prevents app crashes
def run(self):
"""Capture screen and perform OCR."""
try:
# ... existing code ...
except Exception as e:
logger.exception("OCR failed")
self.error_occurred.emit(f"OCR failed: {str(e)}")
finally:
# Always cleanup
self._cleanup_temp_files()
8.1.4 Implement Proper Resource Cleanup
File: core/main.py, core/overlay_window.py
Effort: 4 hours
Impact: Prevents memory leaks
P1 - High Priority (Next Sprint)
8.2.1 Add Comprehensive Logging
Files: All core modules Effort: 12 hours Impact: Debugging, monitoring
# Replace print statements
import logging
logger = logging.getLogger(__name__)
logger.info("Plugin loaded: %s", plugin_name)
logger.warning("Config file corrupted, using defaults")
logger.error("Failed to initialize OCR service", exc_info=True)
8.2.2 Implement Type Hints
Files: Core API surface Effort: 16 hours Impact: Maintainability, IDE support
8.2.3 Add Unit Tests
Files: Core services Effort: 24 hours Impact: Code reliability
Target coverage:
- Event Bus: 90%
- Plugin API: 80%
- OCR Service: 70%
- HTTP Client: 80%
8.2.4 Create Data Access Layer
Files: New core/data/ module
Effort: 16 hours
Impact: Consistency, testability
P2 - Medium Priority (Backlog)
8.3.1 UI/UX Polish
- Loading indicators
- Better error messages
- Consistent styling
8.3.2 Performance Optimizations
- Async HTTP requests
- Lazy plugin loading
- Image caching
8.3.3 Security Hardening
- Plugin sandboxing
- Input sanitization
- Path validation
8.3.4 Documentation
- API documentation
- Plugin development guide
- Architecture decision records
9. Quick Wins (Easy Improvements)
9.1 Low Effort, High Impact
-
Replace print() with logging (2 hours)
- Global search/replace with proper log levels
-
Add docstrings to public APIs (4 hours)
- Focus on
BasePluginandPluginAPI
- Focus on
-
Fix bare except clauses (2 hours)
- Replace
except:withexcept Exception:
- Replace
-
Add all to modules (1 hour)
- Clean up public API surface
-
Sort imports (1 hour)
- Use isort for consistency
9.2 Code Quality Quick Fixes
# Before
except:
pass
# After
except Exception as e:
logger.debug("Operation failed: %s", e)
# Before
def load_data():
if file.exists():
with open(file) as f:
return json.load(f)
# After
def load_data() -> dict:
"""Load data from file."""
if not file.exists():
return {}
try:
with open(file, 'r', encoding='utf-8') as f:
return json.load(f)
except json.JSONDecodeError as e:
logger.error("Corrupted data file: %s", e)
return {}
10. Long-term Architectural Recommendations
10.1 Migration to Async Architecture
Current: Blocking I/O throughout Target: Async/await with Qt event loop integration
# Future architecture
class PluginAPI:
async def nexus_search(self, query: str) -> List[SearchResult]:
"""Async search that doesn't block UI."""
10.2 Plugin Isolation
Current: Plugins run in same process Target: Plugin sandboxing with IPC
Benefits:
- Crash isolation
- Security boundaries
- Hot reloading
10.3 Data Persistence Layer
Current: JSON files Target: SQLite with migrations
# Future data layer
class DataStore:
def __init__(self):
self._db = sqlite3.connect('data/eu_utility.db')
self._migrations = MigrationManager()
10.4 Testing Infrastructure
Current: No tests Target: Comprehensive test suite
tests/
├── unit/
│ ├── core/
│ │ ├── test_event_bus.py
│ │ ├── test_plugin_api.py
│ │ └── test_ocr_service.py
│ └── plugins/
├── integration/
│ └── test_plugin_loading.py
└── e2e/
└── test_hunting_session.py
10.5 CI/CD Pipeline
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: [ubuntu, windows, macos]
steps:
- uses: actions/checkout@v3
- name: Run tests
run: pytest --cov=core tests/
- name: Type check
run: mypy core/
- name: Lint
run: ruff check .
Appendix A: Critical Bugs to Fix Immediately
- Event Bus Race Condition - Can cause crash under load
- OCR Thread Exception - App crashes on OCR failure
- File Path Traversal - Security vulnerability
- Memory Leak in Plugin Reload - UI widgets not deleted
- Unbounded HTTP Cache - Can fill disk
- No Log Rotation - chat.log parsing can hang
- Plugin Init Timeout - Malformed plugins block startup
- Settings Corruption - No backup on parse failure
Appendix B: Migration Guide for Plugin Developers
Version 2.0 to 2.1 Changes
-
New Error Handling Pattern
# Old try: data = self.api.ocr_capture() except: pass # New try: data = self.api.ocr_capture() except OCRServiceError as e: logger.error("OCR failed: %s", e) self.show_error("OCR unavailable") -
Typed Events
# Old self.api.publish_event('loot', {'item': 'Hide'}) # New from core.event_bus import LootEvent self.api.publish_typed(LootEvent( mob_name="Daikiba", items=[{"name": "Animal Hide", "value": 0.03}] ))
Summary
EU-Utility is a well-architected application with a solid plugin system and comprehensive service layer. However, it has accumulated technical debt in error handling, type safety, and testing. The critical issues (race conditions, exception handling, security) should be addressed immediately to ensure stability. The high-priority items (logging, type hints, tests) will significantly improve maintainability.
Estimated effort for full remediation: 120-160 hours Recommended team: 2 developers for 4-6 weeks
Document generated: February 14, 2026
Review scheduled: March 14, 2026