324 lines
7.0 KiB
Markdown
324 lines
7.0 KiB
Markdown
# Plugin Development Guide
|
|
|
|
Create plugins for EU-Utility V3.
|
|
|
|
---
|
|
|
|
## Quick Start
|
|
|
|
### 1. Create Plugin Structure
|
|
|
|
```
|
|
my-plugin/
|
|
plugin.json # Plugin manifest
|
|
index.ts # Main plugin code
|
|
README.md # Documentation
|
|
```
|
|
|
|
### 2. Write Manifest
|
|
|
|
```json
|
|
{
|
|
"manifest_version": "3.0",
|
|
"plugin": {
|
|
"id": "com.example.myplugin",
|
|
"name": "My Plugin",
|
|
"version": "1.0.0",
|
|
"author": "Your Name",
|
|
"description": "What your plugin does",
|
|
"category": "utility"
|
|
},
|
|
"permissions": [
|
|
"log_reader:read",
|
|
"overlay:create",
|
|
"data_store:write"
|
|
],
|
|
"ui": {
|
|
"has_overlay": true,
|
|
"has_settings": true,
|
|
"hotkey": "ctrl+shift+m"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. Write Plugin Code
|
|
|
|
```typescript
|
|
import { BasePlugin, PluginContext } from 'eu-utility-plugin-sdk';
|
|
|
|
export default class MyPlugin extends BasePlugin {
|
|
id = 'com.example.myplugin';
|
|
name = 'My Plugin';
|
|
version = '1.0.0';
|
|
|
|
private context: PluginContext;
|
|
private lootCount = 0;
|
|
|
|
async initialize(context: PluginContext): Promise<boolean> {
|
|
this.context = context;
|
|
|
|
// Subscribe to events
|
|
await this.context.api.subscribeEvent('loot.received', (event) => {
|
|
this.onLoot(event);
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
async activate(): Promise<boolean> {
|
|
this.context.logger.info('My Plugin activated');
|
|
return true;
|
|
}
|
|
|
|
async deactivate(): Promise<void> {
|
|
this.context.logger.info('My Plugin deactivated');
|
|
}
|
|
|
|
private onLoot(event: any) {
|
|
this.lootCount++;
|
|
|
|
// Show notification
|
|
this.context.api.showNotification({
|
|
title: 'Loot Tracked',
|
|
body: `Total loots: ${this.lootCount}`
|
|
});
|
|
|
|
// Save data
|
|
this.context.api.savePluginData(
|
|
this.id,
|
|
'stats',
|
|
{ lootCount: this.lootCount }
|
|
);
|
|
}
|
|
|
|
getSettingsUI(): HTMLElement {
|
|
// Return settings UI element
|
|
const div = document.createElement('div');
|
|
div.innerHTML = `
|
|
<label>Enable Feature</label>
|
|
<input type="checkbox" id="feature-toggle" />
|
|
`;
|
|
return div;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## BasePlugin Class
|
|
|
|
```typescript
|
|
abstract class BasePlugin {
|
|
abstract id: string;
|
|
abstract name: string;
|
|
abstract version: string;
|
|
|
|
// Required methods
|
|
abstract initialize(context: PluginContext): Promise<boolean>;
|
|
abstract activate(): Promise<boolean>;
|
|
abstract deactivate(): Promise<void>;
|
|
|
|
// Optional methods
|
|
getOverlayUI?(): HTMLElement;
|
|
getSettingsUI?(): HTMLElement;
|
|
onHotkey?(hotkey: string): void;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Permissions
|
|
|
|
Available permissions:
|
|
|
|
| Permission | Description |
|
|
|------------|-------------|
|
|
| `log_reader:read` | Read game logs |
|
|
| `overlay:create` | Create overlay widgets |
|
|
| `data_store:write` | Save plugin data |
|
|
| `data_store:read` | Read plugin data |
|
|
| `network:external` | Make HTTP requests |
|
|
| `ocr:read` | Use OCR service |
|
|
| `clipboard:read` | Read clipboard |
|
|
| `clipboard:write` | Write to clipboard |
|
|
| `notification:send` | Send notifications |
|
|
|
|
---
|
|
|
|
## Example: Loot Tracker
|
|
|
|
Complete example plugin:
|
|
|
|
```typescript
|
|
import { BasePlugin, PluginContext } from 'eu-utility-plugin-sdk';
|
|
|
|
export default class LootTracker extends BasePlugin {
|
|
id = 'eu.utility.loot-tracker';
|
|
name = 'Loot Tracker';
|
|
version = '2.0.0';
|
|
author = 'Aether';
|
|
description = 'Track hunting loot with ROI analysis';
|
|
category = 'tracking';
|
|
|
|
private context: PluginContext;
|
|
private sessionStart = Date.now();
|
|
private totalLoot = 0;
|
|
private mobCount = 0;
|
|
|
|
async initialize(context: PluginContext): Promise<boolean> {
|
|
this.context = context;
|
|
|
|
// Load saved data
|
|
const saved = await context.api.loadPluginData(this.id, 'session');
|
|
if (saved) {
|
|
this.totalLoot = saved.totalLoot || 0;
|
|
this.mobCount = saved.mobCount || 0;
|
|
}
|
|
|
|
// Subscribe to loot events
|
|
await context.api.subscribeEvent('loot.received', (e) => {
|
|
this.handleLoot(e.data);
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
async activate(): Promise<boolean> {
|
|
this.sessionStart = Date.now();
|
|
this.context.logger.info('Loot Tracker: Session started');
|
|
|
|
// Create overlay widget
|
|
await this.createWidget();
|
|
|
|
return true;
|
|
}
|
|
|
|
async deactivate(): Promise<void> {
|
|
// Save session data
|
|
await this.context.api.savePluginData(this.id, 'session', {
|
|
totalLoot: this.totalLoot,
|
|
mobCount: this.mobCount,
|
|
duration: Date.now() - this.sessionStart
|
|
});
|
|
}
|
|
|
|
private async handleLoot(data: LootData) {
|
|
this.totalLoot += data.totalTT;
|
|
this.mobCount++;
|
|
|
|
// Update widget
|
|
await this.updateWidget();
|
|
|
|
// Notify on big loot
|
|
if (data.totalTT > 50) {
|
|
await this.context.api.showNotification({
|
|
title: 'Big Loot!',
|
|
body: `${data.mobName}: ${data.totalTT.toFixed(2)} PED`,
|
|
sound: true
|
|
});
|
|
}
|
|
|
|
// Log to file
|
|
await this.logLoot(data);
|
|
}
|
|
|
|
private async createWidget() {
|
|
await this.context.widget.create({
|
|
id: 'loot-tracker-main',
|
|
title: 'Loot Tracker',
|
|
width: 280,
|
|
height: 150,
|
|
x: 50,
|
|
y: 50,
|
|
content: this.renderWidget()
|
|
});
|
|
}
|
|
|
|
private renderWidget(): string {
|
|
const elapsed = Math.floor((Date.now() - this.sessionStart) / 1000);
|
|
const hours = Math.floor(elapsed / 3600);
|
|
const mins = Math.floor((elapsed % 3600) / 60);
|
|
const secs = elapsed % 60;
|
|
|
|
return `
|
|
<div style="font-family: system-ui; color: #fff; padding: 12px;">
|
|
<div style="font-size: 14px; font-weight: 600; margin-bottom: 8px;">
|
|
Session: ${hours}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}
|
|
</div>
|
|
<div style="font-size: 24px; font-weight: 700; color: #10b981;">
|
|
${this.totalLoot.toFixed(2)} PED
|
|
</div>
|
|
<div style="font-size: 12px; color: #94a3b8; margin-top: 4px;">
|
|
${this.mobCount} mobs killed
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
private async updateWidget() {
|
|
await this.context.widget.update('loot-tracker-main', {
|
|
content: this.renderWidget()
|
|
});
|
|
}
|
|
|
|
private async logLoot(data: LootData) {
|
|
const entry = {
|
|
timestamp: new Date().toISOString(),
|
|
mob: data.mobName,
|
|
tt: data.totalTT,
|
|
items: data.items.length
|
|
};
|
|
|
|
const logs = await this.context.api.loadPluginData(this.id, 'logs') || [];
|
|
logs.push(entry);
|
|
|
|
// Keep last 1000 entries
|
|
if (logs.length > 1000) logs.shift();
|
|
|
|
await this.context.api.savePluginData(this.id, 'logs', logs);
|
|
}
|
|
|
|
onHotkey(hotkey: string): void {
|
|
if (hotkey === 'ctrl+shift+l') {
|
|
this.context.api.showNotification({
|
|
title: 'Loot Stats',
|
|
body: `Total: ${this.totalLoot.toFixed(2)} PED in ${this.mobCount} kills`
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
interface LootData {
|
|
mobName: string;
|
|
totalTT: number;
|
|
items: any[];
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Building Plugins
|
|
|
|
```bash
|
|
# Install SDK
|
|
npm install eu-utility-plugin-sdk
|
|
|
|
# Build
|
|
npm run build
|
|
|
|
# Package
|
|
eu-util plugin package
|
|
```
|
|
|
|
---
|
|
|
|
## Publishing
|
|
|
|
1. Create release on Git
|
|
2. Tag with version
|
|
3. Attach plugin.zip
|
|
|
|
---
|
|
|
|
*Plugin Development Guide - EU-Utility V3*
|