714 lines
18 KiB
Markdown
714 lines
18 KiB
Markdown
# EU-Utility Application Redesign Draft
|
|
## Version 3.0 - Professional Architecture
|
|
|
|
---
|
|
|
|
## 🎯 Executive Summary
|
|
|
|
Current EU-Utility is feature-rich but architecture is fragmented. This redesign creates a unified, scalable, maintainable application.
|
|
|
|
**Key Improvements:**
|
|
- Clean Architecture (Domain-Driven Design)
|
|
- Unified API Gateway
|
|
- Reactive Event System
|
|
- Plugin Sandbox Security
|
|
- Cloud Sync Capabilities
|
|
|
|
---
|
|
|
|
## 🏗️ New Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ PRESENTATION LAYER │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
|
│ │ Main UI │ │ Overlay │ │ System Tray │ │
|
|
│ │ (PyQt6) │ │ Manager │ │ Icon │ │
|
|
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
|
|
└─────────┼────────────────┼────────────────────┼─────────────┘
|
|
│ │ │
|
|
└────────────────┴────────────────────┘
|
|
│
|
|
┌────────▼────────┐
|
|
│ API GATEWAY │
|
|
│ (FastAPI/REST) │
|
|
└────────┬────────┘
|
|
│
|
|
┌────────────────────┼────────────────────┐
|
|
│ │ │
|
|
┌───────▼──────┐ ┌─────────▼─────────┐ ┌──────▼──────┐
|
|
│ CORE │ │ PLUGIN ENGINE │ │ SERVICES │
|
|
│ SERVICES │ │ │ │ │
|
|
│ │ │ ┌─────────────┐ │ │ • Nexus │
|
|
│ • Settings │ │ │ Plugin │ │ │ • OCR │
|
|
│ • DataStore │ │ │ Manager │ │ │ • Overlay │
|
|
│ • EventBus │ │ └──────┬──────┘ │ │ • Capture │
|
|
│ • Auth │ │ │ │ └─────────────┘
|
|
└──────────────┘ │ ┌──────▼──────┐ │
|
|
│ │ Sandboxed │ │
|
|
│ │ Plugins │ │
|
|
│ └─────────────┘ │
|
|
└───────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 📦 Domain Modules
|
|
|
|
### 1. Domain: Core
|
|
|
|
```python
|
|
# domain/core/entities.py
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import Optional, Dict, Any
|
|
from enum import Enum
|
|
|
|
class PluginState(Enum):
|
|
INSTALLED = "installed"
|
|
ACTIVE = "active"
|
|
ERROR = "error"
|
|
DISABLED = "disabled"
|
|
|
|
@dataclass
|
|
class Plugin:
|
|
"""Core plugin entity."""
|
|
id: str
|
|
name: str
|
|
version: str
|
|
author: str
|
|
description: str
|
|
state: PluginState
|
|
hotkey: Optional[str] = None
|
|
config: Dict[str, Any] = None
|
|
created_at: datetime = None
|
|
updated_at: datetime = None
|
|
|
|
@dataclass
|
|
class UserSettings:
|
|
"""User configuration entity."""
|
|
user_id: str
|
|
hotkeys: Dict[str, str]
|
|
theme: str
|
|
plugins_enabled: list
|
|
overlay_position: tuple
|
|
ocr_engine: str
|
|
api_keys: Dict[str, str]
|
|
```
|
|
|
|
### 2. Domain: Game
|
|
|
|
```python
|
|
# domain/game/entities.py
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
from decimal import Decimal
|
|
|
|
@dataclass
|
|
class LootItem:
|
|
"""Represents a looted item."""
|
|
name: str
|
|
quantity: int
|
|
tt_value: Decimal
|
|
market_value: Optional[Decimal] = None
|
|
|
|
@dataclass
|
|
class LootEvent:
|
|
"""Game loot event."""
|
|
id: str
|
|
timestamp: datetime
|
|
mob_name: str
|
|
items: List[LootItem]
|
|
total_tt: Decimal
|
|
location: Optional[tuple] = None
|
|
|
|
@dataclass
|
|
class SkillGain:
|
|
"""Skill increase event."""
|
|
skill_name: str
|
|
value_before: float
|
|
value_after: float
|
|
timestamp: datetime
|
|
```
|
|
|
|
### 3. Domain: Market
|
|
|
|
```python
|
|
# domain/market/entities.py
|
|
from dataclasses import dataclass
|
|
from decimal import Decimal
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
@dataclass
|
|
class MarketItem:
|
|
"""Market data for an item."""
|
|
item_id: str
|
|
name: str
|
|
category: str
|
|
current_price: Decimal
|
|
markup_percent: Decimal
|
|
volume_24h: int
|
|
last_update: datetime
|
|
|
|
@dataclass
|
|
class PriceAlert:
|
|
"""User price alert."""
|
|
item_id: str
|
|
user_id: str
|
|
target_price: Decimal
|
|
condition: str # 'above' or 'below'
|
|
is_active: bool
|
|
```
|
|
|
|
---
|
|
|
|
## 🔌 Plugin System Redesign
|
|
|
|
### Plugin Manifest (v3.0)
|
|
|
|
```json
|
|
{
|
|
"manifest_version": "3.0",
|
|
"plugin": {
|
|
"id": "com.example.loot_tracker",
|
|
"name": "Advanced Loot Tracker",
|
|
"version": "2.1.0",
|
|
"author": "ImpulsiveFPS",
|
|
"description": "Professional loot tracking with analytics",
|
|
"category": "tracking",
|
|
"min_api_version": "3.0"
|
|
},
|
|
"permissions": [
|
|
"log_reader:read",
|
|
"overlay:create",
|
|
"data_store:write",
|
|
"notification:send",
|
|
"ocr:read"
|
|
],
|
|
"resources": {
|
|
"memory_limit": "128MB",
|
|
"cpu_limit": "10%",
|
|
"storage_limit": "50MB"
|
|
},
|
|
"ui": {
|
|
"has_overlay": true,
|
|
"has_settings": true,
|
|
"hotkey": "ctrl+shift+l",
|
|
"icon": "treasure-chest"
|
|
},
|
|
"dependencies": {
|
|
"core": ">=3.0",
|
|
"plugins": []
|
|
}
|
|
}
|
|
```
|
|
|
|
### Plugin Base Class
|
|
|
|
```python
|
|
# application/plugin_base.py
|
|
from abc import ABC, abstractmethod
|
|
from typing import Dict, Any, Optional
|
|
from dataclasses import dataclass
|
|
import asyncio
|
|
|
|
@dataclass
|
|
class PluginContext:
|
|
"""Context provided to plugins."""
|
|
api: 'PluginAPI'
|
|
settings: Dict[str, Any]
|
|
storage: 'PluginStorage'
|
|
logger: 'PluginLogger'
|
|
event_bus: 'EventBus'
|
|
|
|
class BasePlugin(ABC):
|
|
"""Base class for all EU-Utility plugins."""
|
|
|
|
# Required - defined in manifest
|
|
id: str
|
|
name: str
|
|
version: str
|
|
|
|
# Optional - defined in manifest
|
|
description: str = ""
|
|
author: str = ""
|
|
category: str = "utility"
|
|
|
|
def __init__(self, context: PluginContext):
|
|
self.ctx = context
|
|
self._state = PluginState.INSTALLED
|
|
self._config: Dict[str, Any] = {}
|
|
|
|
@abstractmethod
|
|
async def initialize(self) -> bool:
|
|
"""Initialize the plugin. Return True on success."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def activate(self) -> bool:
|
|
"""Called when plugin is activated."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
async def deactivate(self) -> None:
|
|
"""Called when plugin is deactivated."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_ui(self) -> Optional['QWidget']:
|
|
"""Return the plugin's UI component."""
|
|
pass
|
|
|
|
async def on_event(self, event: 'Event') -> None:
|
|
"""Handle events from the event bus."""
|
|
pass
|
|
|
|
def get_config_schema(self) -> Dict[str, Any]:
|
|
"""Return JSON schema for plugin configuration."""
|
|
return {}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 Security Architecture
|
|
|
|
### Permission System
|
|
|
|
```python
|
|
# security/permissions.py
|
|
from enum import Enum, auto
|
|
|
|
class PermissionScope(Enum):
|
|
"""Permission scopes for plugins."""
|
|
LOG_READER = auto()
|
|
OVERLAY = auto()
|
|
DATA_STORE = auto()
|
|
NETWORK = auto()
|
|
OCR = auto()
|
|
CLIPBOARD = auto()
|
|
FILE_SYSTEM = auto()
|
|
PROCESS = auto()
|
|
|
|
class PermissionLevel(Enum):
|
|
"""Permission levels."""
|
|
NONE = 0
|
|
READ = 1
|
|
WRITE = 2
|
|
ADMIN = 3
|
|
|
|
@dataclass
|
|
class Permission:
|
|
scope: PermissionScope
|
|
level: PermissionLevel
|
|
constraints: Dict[str, Any] = None
|
|
```
|
|
|
|
### Plugin Sandbox
|
|
|
|
```python
|
|
# security/sandbox.py
|
|
import subprocess
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
class PluginSandbox:
|
|
"""Isolated execution environment for plugins."""
|
|
|
|
def __init__(self, plugin_id: str, permissions: List[Permission]):
|
|
self.plugin_id = plugin_id
|
|
self.permissions = permissions
|
|
self._temp_dir = tempfile.mkdtemp(prefix=f"eu_plugin_{plugin_id}_")
|
|
|
|
def execute(self, code: str) -> Any:
|
|
"""Execute plugin code in restricted environment."""
|
|
# Implementation uses restricted Python interpreter
|
|
# with limited builtins and custom __import__
|
|
pass
|
|
|
|
def cleanup(self) -> None:
|
|
"""Clean up sandbox resources."""
|
|
shutil.rmtree(self._temp_dir, ignore_errors=True)
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 Event System
|
|
|
|
### Reactive Event Bus
|
|
|
|
```python
|
|
# infrastructure/event_bus.py
|
|
from typing import Callable, TypeVar, Generic
|
|
import asyncio
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
|
|
T = TypeVar('T')
|
|
|
|
@dataclass
|
|
class Event(Generic[T]):
|
|
"""Generic event container."""
|
|
type: str
|
|
payload: T
|
|
timestamp: datetime
|
|
source: str
|
|
|
|
class ReactiveEventBus:
|
|
"""Reactive event system with filtering and transformation."""
|
|
|
|
def __init__(self):
|
|
self._subscribers: Dict[str, List[Callable]] = {}
|
|
self._streams: Dict[str, 'EventStream'] = {}
|
|
|
|
def subscribe(self,
|
|
event_type: str,
|
|
handler: Callable[[Event], None],
|
|
filter_fn: Optional[Callable[[Event], bool]] = None) -> str:
|
|
"""Subscribe to events with optional filtering."""
|
|
subscription_id = str(uuid.uuid4())
|
|
# Implementation
|
|
return subscription_id
|
|
|
|
def publish(self, event: Event) -> None:
|
|
"""Publish event to all subscribers."""
|
|
# Async dispatch
|
|
asyncio.create_task(self._dispatch(event))
|
|
|
|
def stream(self, event_type: str) -> 'EventStream':
|
|
"""Get reactive stream for event type."""
|
|
if event_type not in self._streams:
|
|
self._streams[event_type] = EventStream(event_type)
|
|
return self._streams[event_type]
|
|
|
|
class EventStream:
|
|
"""Reactive stream for events."""
|
|
|
|
def __init__(self, event_type: str):
|
|
self.event_type = event_type
|
|
self._handlers = []
|
|
|
|
def filter(self, predicate: Callable[[Event], bool]) -> 'EventStream':
|
|
"""Filter events."""
|
|
return self
|
|
|
|
def map(self, transform: Callable[[Event], T]) -> 'EventStream':
|
|
"""Transform events."""
|
|
return self
|
|
|
|
def debounce(self, seconds: float) -> 'EventStream':
|
|
"""Debounce events."""
|
|
return self
|
|
|
|
def subscribe(self, handler: Callable[[T], None]) -> None:
|
|
"""Subscribe to stream."""
|
|
pass
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Data Layer
|
|
|
|
### Repository Pattern
|
|
|
|
```python
|
|
# infrastructure/repositories.py
|
|
from abc import ABC, abstractmethod
|
|
from typing import List, Optional, Generic, TypeVar
|
|
|
|
T = TypeVar('T')
|
|
ID = TypeVar('ID')
|
|
|
|
class Repository(ABC, Generic[T, ID]):
|
|
"""Base repository interface."""
|
|
|
|
@abstractmethod
|
|
def get(self, id: ID) -> Optional[T]:
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_all(self) -> List[T]:
|
|
pass
|
|
|
|
@abstractmethod
|
|
def create(self, entity: T) -> T:
|
|
pass
|
|
|
|
@abstractmethod
|
|
def update(self, entity: T) -> T:
|
|
pass
|
|
|
|
@abstractmethod
|
|
def delete(self, id: ID) -> bool:
|
|
pass
|
|
|
|
class SQLiteRepository(Repository[T, ID]):
|
|
"""SQLite implementation."""
|
|
|
|
def __init__(self, db_path: str, entity_class: type):
|
|
self.db_path = db_path
|
|
self.entity_class = entity_class
|
|
self._init_db()
|
|
```
|
|
|
|
### Data Sync
|
|
|
|
```python
|
|
# infrastructure/sync.py
|
|
from typing import Dict, Any
|
|
import aiohttp
|
|
|
|
class CloudSync:
|
|
"""Sync data with cloud backend."""
|
|
|
|
def __init__(self, api_base: str, api_key: str):
|
|
self.api_base = api_base
|
|
self.api_key = api_key
|
|
self._local_queue = []
|
|
|
|
async def sync_settings(self, settings: Dict[str, Any]) -> bool:
|
|
"""Sync user settings to cloud."""
|
|
async with aiohttp.ClientSession() as session:
|
|
async with session.post(
|
|
f"{self.api_base}/settings",
|
|
json=settings,
|
|
headers={"Authorization": f"Bearer {self.api_key}"}
|
|
) as resp:
|
|
return resp.status == 200
|
|
|
|
async def get_remote_settings(self) -> Dict[str, Any]:
|
|
"""Get settings from cloud."""
|
|
pass
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 API Gateway
|
|
|
|
### RESTful API
|
|
|
|
```python
|
|
# api/gateway.py
|
|
from fastapi import FastAPI, Depends, HTTPException
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from typing import List, Optional
|
|
|
|
app = FastAPI(
|
|
title="EU-Utility API",
|
|
version="3.0.0",
|
|
description="Unified API for EU-Utility"
|
|
)
|
|
|
|
# Authentication dependency
|
|
async def verify_token(token: str = Header(...)):
|
|
"""Verify API token."""
|
|
# Implementation
|
|
pass
|
|
|
|
@app.get("/plugins", response_model=List[PluginSchema])
|
|
async def list_plugins(
|
|
category: Optional[str] = None,
|
|
active_only: bool = True,
|
|
auth = Depends(verify_token)
|
|
):
|
|
"""List all plugins."""
|
|
pass
|
|
|
|
@app.post("/plugins/{plugin_id}/activate")
|
|
async def activate_plugin(
|
|
plugin_id: str,
|
|
auth = Depends(verify_token)
|
|
):
|
|
"""Activate a plugin."""
|
|
pass
|
|
|
|
@app.get("/market/search")
|
|
async def search_market(
|
|
query: str,
|
|
category: Optional[str] = None,
|
|
limit: int = 20
|
|
):
|
|
"""Search market items."""
|
|
pass
|
|
|
|
@app.get("/game/loot/recent")
|
|
async def get_recent_loot(
|
|
hours: int = 24,
|
|
auth = Depends(verify_token)
|
|
):
|
|
"""Get recent loot events."""
|
|
pass
|
|
|
|
@app.post("/ocr/recognize")
|
|
async def ocr_recognize(
|
|
region: tuple, # (x, y, width, height)
|
|
auth = Depends(verify_token)
|
|
):
|
|
"""Recognize text from screen region."""
|
|
pass
|
|
```
|
|
|
|
---
|
|
|
|
## 📱 UI/UX Design
|
|
|
|
### Component Library
|
|
|
|
```python
|
|
# ui/components.py
|
|
from PyQt6.QtWidgets import *
|
|
from PyQt6.QtCore import *
|
|
from PyQt6.QtGui import *
|
|
|
|
class EUCard(QFrame):
|
|
"""Card component for EU-Utility."""
|
|
|
|
def __init__(self, title: str, parent=None):
|
|
super().__init__(parent)
|
|
self.setObjectName("EUCard")
|
|
self.setup_ui(title)
|
|
|
|
def setup_ui(self, title: str):
|
|
layout = QVBoxLayout(self)
|
|
layout.setContentsMargins(16, 16, 16, 16)
|
|
|
|
# Header
|
|
header = QLabel(title)
|
|
header.setObjectName("card-title")
|
|
layout.addWidget(header)
|
|
|
|
# Content area
|
|
self.content = QFrame()
|
|
self.content_layout = QVBoxLayout(self.content)
|
|
layout.addWidget(self.content)
|
|
|
|
# Styling
|
|
self.setStyleSheet("""
|
|
#EUCard {
|
|
background-color: #2d2d2d;
|
|
border-radius: 12px;
|
|
border: 1px solid #3d3d3d;
|
|
}
|
|
#card-title {
|
|
color: #ffffff;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
margin-bottom: 12px;
|
|
}
|
|
""")
|
|
|
|
class EUButton(QPushButton):
|
|
"""Primary button component."""
|
|
|
|
def __init__(self, text: str, variant: str = "primary"):
|
|
super().__init__(text)
|
|
self.variant = variant
|
|
self.setup_style()
|
|
|
|
def setup_style(self):
|
|
colors = {
|
|
"primary": ("#6366f1", "#4f46e5"),
|
|
"secondary": ("#3d3d3d", "#4d4d4d"),
|
|
"danger": ("#ef4444", "#dc2626"),
|
|
"success": ("#10b981", "#059669")
|
|
}
|
|
bg, hover = colors.get(self.variant, colors["primary"])
|
|
|
|
self.setStyleSheet(f"""
|
|
EUButton {{
|
|
background-color: {bg};
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 10px 20px;
|
|
font-weight: 600;
|
|
}}
|
|
EUButton:hover {{
|
|
background-color: {hover};
|
|
}}
|
|
""")
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 Implementation Roadmap
|
|
|
|
### Phase 1: Foundation (Week 1-2)
|
|
- [ ] Set up new project structure
|
|
- [ ] Implement domain entities
|
|
- [ ] Create repository layer
|
|
- [ ] Set up dependency injection
|
|
|
|
### Phase 2: Core Services (Week 3-4)
|
|
- [ ] Event bus implementation
|
|
- [ ] Plugin system v3.0
|
|
- [ ] Security sandbox
|
|
- [ ] Settings management
|
|
|
|
### Phase 3: API & UI (Week 5-6)
|
|
- [ ] API Gateway
|
|
- [ ] Component library
|
|
- [ ] Main UI refactoring
|
|
- [ ] Overlay system
|
|
|
|
### Phase 4: Integration (Week 7-8)
|
|
- [ ] Plugin migration tool
|
|
- [ ] Data migration
|
|
- [ ] Testing suite
|
|
- [ ] Documentation
|
|
|
|
### Phase 5: Release (Week 9)
|
|
- [ ] Beta testing
|
|
- [ ] Performance optimization
|
|
- [ ] Security audit
|
|
- [ ] Public release
|
|
|
|
---
|
|
|
|
## 📊 Migration Strategy
|
|
|
|
### From v2.x to v3.0
|
|
|
|
```python
|
|
# migration/v2_to_v3.py
|
|
class MigrationTool:
|
|
"""Migrate plugins and data from v2 to v3."""
|
|
|
|
def migrate_plugin(self, v2_plugin_path: str) -> str:
|
|
"""Convert v2 plugin to v3 format."""
|
|
# Read v2 plugin
|
|
# Generate v3 manifest
|
|
# Update imports
|
|
# Return new plugin path
|
|
pass
|
|
|
|
def migrate_settings(self, v2_settings: dict) -> dict:
|
|
"""Migrate user settings."""
|
|
mapping = {
|
|
"hotkeys.global_toggle": "hotkeys.overlay_toggle",
|
|
"plugins.enabled": "plugins.active",
|
|
# ...
|
|
}
|
|
# Apply mapping
|
|
return migrated_settings
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 Success Metrics
|
|
|
|
| Metric | Target |
|
|
|--------|--------|
|
|
| Startup Time | < 2 seconds |
|
|
| Memory Usage | < 200MB |
|
|
| Plugin Load | < 100ms each |
|
|
| API Response | < 50ms |
|
|
| Code Coverage | > 80% |
|
|
| Plugin Compatibility | 100% v2 plugins work |
|
|
|
|
---
|
|
|
|
*Draft Application Design - EU-Utility v3.0*
|
|
*Created: 2026-02-23*
|