442 lines
11 KiB
JavaScript
442 lines
11 KiB
JavaScript
/**
|
|
* AeThex Connect Plugin for Nexus Engine
|
|
* Integrates AeThex Connect communication into games
|
|
*
|
|
* @version 1.0.0
|
|
* @license MIT
|
|
*/
|
|
|
|
class AeThexConnectPlugin {
|
|
constructor(nexusEngine, config = {}) {
|
|
this.nexus = nexusEngine;
|
|
this.config = {
|
|
apiUrl: config.apiUrl || 'https://connect.aethex.app/api',
|
|
apiKey: config.apiKey,
|
|
enableOverlay: config.enableOverlay !== false,
|
|
enableNotifications: config.enableNotifications !== false,
|
|
overlayPosition: config.overlayPosition || 'top-right',
|
|
autoMute: config.autoMute !== false,
|
|
...config
|
|
};
|
|
|
|
this.sessionId = null;
|
|
this.overlayConfig = null;
|
|
this.overlayElement = null;
|
|
this.isInitialized = false;
|
|
}
|
|
|
|
/**
|
|
* Initialize plugin
|
|
*/
|
|
async initialize() {
|
|
try {
|
|
console.log('[AeThex Connect] Initializing...');
|
|
|
|
// Get player info from Nexus
|
|
const player = await this.nexus.getPlayer();
|
|
|
|
if (!player || !player.id) {
|
|
throw new Error('Failed to get player information from Nexus');
|
|
}
|
|
|
|
// Start game session with AeThex Connect
|
|
const response = await fetch(`${this.config.apiUrl}/nexus/sessions/start`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Nexus-API-Key': this.config.apiKey,
|
|
'X-Nexus-Player-ID': player.id
|
|
},
|
|
body: JSON.stringify({
|
|
nexusPlayerId: player.id,
|
|
gameId: this.nexus.gameId,
|
|
gameName: this.nexus.gameName,
|
|
metadata: {
|
|
platform: this.nexus.platform || 'unknown',
|
|
playerName: player.displayName || player.username
|
|
}
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
this.sessionId = data.session.id;
|
|
this.overlayConfig = data.overlayConfig;
|
|
this.isInitialized = true;
|
|
|
|
// Initialize overlay if enabled
|
|
if (this.overlayConfig.enabled && this.config.enableOverlay) {
|
|
this.initializeOverlay();
|
|
}
|
|
|
|
// Setup event listeners
|
|
this.setupEventListeners();
|
|
|
|
console.log('[AeThex Connect] Initialized successfully');
|
|
console.log('[AeThex Connect] Session ID:', this.sessionId);
|
|
|
|
return true;
|
|
} else {
|
|
throw new Error(data.error || 'Failed to start session');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('[AeThex Connect] Initialization failed:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setup event listeners for game state changes
|
|
*/
|
|
setupEventListeners() {
|
|
// When player enters match
|
|
this.nexus.on('match:start', async (matchData) => {
|
|
console.log('[AeThex Connect] Match started');
|
|
|
|
await this.updateSessionState('in-match', {
|
|
mapName: matchData.map,
|
|
gameMode: matchData.mode,
|
|
teamId: matchData.teamId
|
|
});
|
|
|
|
// Auto-mute if configured
|
|
if (this.overlayConfig.autoMute) {
|
|
this.triggerAutoMute();
|
|
}
|
|
});
|
|
|
|
// When player returns to menu
|
|
this.nexus.on('match:end', async (matchData) => {
|
|
console.log('[AeThex Connect] Match ended');
|
|
|
|
await this.updateSessionState('in-menu', {
|
|
score: matchData.score,
|
|
won: matchData.won,
|
|
duration: matchData.duration
|
|
});
|
|
|
|
// Unmute
|
|
if (this.overlayConfig.autoMute) {
|
|
this.triggerAutoUnmute();
|
|
}
|
|
});
|
|
|
|
// When game closes
|
|
this.nexus.on('game:exit', async () => {
|
|
console.log('[AeThex Connect] Game exiting');
|
|
await this.endSession();
|
|
});
|
|
|
|
// When player pauses
|
|
this.nexus.on('game:pause', async () => {
|
|
await this.updateSessionState('paused');
|
|
});
|
|
|
|
// When player resumes
|
|
this.nexus.on('game:resume', async () => {
|
|
await this.updateSessionState('active');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update session state
|
|
*/
|
|
async updateSessionState(state, metadata = {}) {
|
|
if (!this.sessionId || !this.isInitialized) return;
|
|
|
|
try {
|
|
await fetch(`${this.config.apiUrl}/nexus/sessions/${this.sessionId}/update`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Nexus-API-Key': this.config.apiKey
|
|
},
|
|
body: JSON.stringify({ state, metadata })
|
|
});
|
|
} catch (error) {
|
|
console.error('[AeThex Connect] Failed to update session:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* End session
|
|
*/
|
|
async endSession() {
|
|
if (!this.sessionId || !this.isInitialized) return;
|
|
|
|
try {
|
|
await fetch(`${this.config.apiUrl}/nexus/sessions/${this.sessionId}/end`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Nexus-API-Key': this.config.apiKey
|
|
},
|
|
body: JSON.stringify({
|
|
duration: this.getSessionDuration(),
|
|
metadata: {}
|
|
})
|
|
});
|
|
|
|
this.sessionId = null;
|
|
this.isInitialized = false;
|
|
|
|
// Remove overlay
|
|
if (this.overlayElement) {
|
|
this.overlayElement.remove();
|
|
this.overlayElement = null;
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('[AeThex Connect] Failed to end session:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize in-game overlay
|
|
*/
|
|
initializeOverlay() {
|
|
// Create iframe overlay
|
|
const overlay = document.createElement('iframe');
|
|
overlay.id = 'aethex-connect-overlay';
|
|
overlay.src = `${this.config.apiUrl.replace('/api', '')}/overlay?session=${this.sessionId}`;
|
|
|
|
// Position based on config
|
|
const positions = {
|
|
'top-right': 'top: 20px; right: 20px;',
|
|
'top-left': 'top: 20px; left: 20px;',
|
|
'bottom-right': 'bottom: 20px; right: 20px;',
|
|
'bottom-left': 'bottom: 20px; left: 20px;'
|
|
};
|
|
|
|
overlay.style.cssText = `
|
|
position: fixed;
|
|
${positions[this.config.overlayPosition] || positions['top-right']}
|
|
width: 320px;
|
|
height: 480px;
|
|
border: none;
|
|
z-index: 999999;
|
|
opacity: ${this.overlayConfig.opacity || 0.9};
|
|
pointer-events: auto;
|
|
transition: all 0.3s ease;
|
|
`;
|
|
|
|
// Add to DOM
|
|
document.body.appendChild(overlay);
|
|
this.overlayElement = overlay;
|
|
|
|
// Listen for overlay messages
|
|
window.addEventListener('message', (event) => {
|
|
// Security check
|
|
const allowedOrigins = [
|
|
'https://connect.aethex.app',
|
|
'http://localhost:3000',
|
|
'http://localhost:5173'
|
|
];
|
|
|
|
if (!allowedOrigins.includes(event.origin)) return;
|
|
|
|
this.handleOverlayMessage(event.data);
|
|
});
|
|
|
|
console.log('[AeThex Connect] Overlay initialized');
|
|
}
|
|
|
|
/**
|
|
* Handle overlay messages
|
|
*/
|
|
handleOverlayMessage(message) {
|
|
switch (message.type) {
|
|
case 'minimize':
|
|
this.minimizeOverlay(message.minimized);
|
|
break;
|
|
case 'notification':
|
|
this.showNotification(message.data);
|
|
break;
|
|
case 'friend_invite':
|
|
this.handleFriendInvite(message.data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Minimize/restore overlay
|
|
*/
|
|
minimizeOverlay(minimized) {
|
|
if (!this.overlayElement) return;
|
|
|
|
if (minimized) {
|
|
this.overlayElement.style.width = '60px';
|
|
this.overlayElement.style.height = '60px';
|
|
} else {
|
|
this.overlayElement.style.width = '320px';
|
|
this.overlayElement.style.height = '480px';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show in-game notification
|
|
*/
|
|
showNotification(notification) {
|
|
if (!this.config.enableNotifications) return;
|
|
|
|
// Create notification element
|
|
const notif = document.createElement('div');
|
|
notif.className = 'aethex-notification';
|
|
notif.innerHTML = `
|
|
<div class="notif-icon">${notification.icon || '💬'}</div>
|
|
<div class="notif-content">
|
|
<div class="notif-title">${this.escapeHtml(notification.title)}</div>
|
|
<div class="notif-body">${this.escapeHtml(notification.body)}</div>
|
|
</div>
|
|
`;
|
|
|
|
// Add styles if not already added
|
|
this.injectNotificationStyles();
|
|
|
|
document.body.appendChild(notif);
|
|
|
|
// Auto-remove after 5 seconds
|
|
setTimeout(() => {
|
|
notif.style.opacity = '0';
|
|
setTimeout(() => notif.remove(), 300);
|
|
}, 5000);
|
|
|
|
console.log('[AeThex Connect] Notification shown:', notification.title);
|
|
}
|
|
|
|
/**
|
|
* Inject notification styles
|
|
*/
|
|
injectNotificationStyles() {
|
|
if (document.getElementById('aethex-notif-styles')) return;
|
|
|
|
const style = document.createElement('style');
|
|
style.id = 'aethex-notif-styles';
|
|
style.textContent = `
|
|
.aethex-notification {
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
width: 320px;
|
|
background: rgba(20, 20, 30, 0.95);
|
|
backdrop-filter: blur(10px);
|
|
border-radius: 8px;
|
|
padding: 16px;
|
|
display: flex;
|
|
gap: 12px;
|
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
|
|
animation: slideIn 0.3s ease-out;
|
|
z-index: 1000000;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
transition: opacity 0.3s;
|
|
}
|
|
@keyframes slideIn {
|
|
from {
|
|
transform: translateX(400px);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateX(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
.aethex-notification .notif-icon {
|
|
font-size: 24px;
|
|
flex-shrink: 0;
|
|
}
|
|
.aethex-notification .notif-content {
|
|
flex: 1;
|
|
}
|
|
.aethex-notification .notif-title {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #fff;
|
|
margin-bottom: 4px;
|
|
}
|
|
.aethex-notification .notif-body {
|
|
font-size: 13px;
|
|
color: #aaa;
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
/**
|
|
* Trigger auto-mute for voice chat
|
|
*/
|
|
triggerAutoMute() {
|
|
if (!this.overlayElement) return;
|
|
|
|
this.overlayElement.contentWindow.postMessage({
|
|
type: 'auto_mute',
|
|
mute: true
|
|
}, '*');
|
|
|
|
console.log('[AeThex Connect] Auto-mute triggered');
|
|
}
|
|
|
|
/**
|
|
* Trigger auto-unmute
|
|
*/
|
|
triggerAutoUnmute() {
|
|
if (!this.overlayElement) return;
|
|
|
|
this.overlayElement.contentWindow.postMessage({
|
|
type: 'auto_mute',
|
|
mute: false
|
|
}, '*');
|
|
|
|
console.log('[AeThex Connect] Auto-unmute triggered');
|
|
}
|
|
|
|
/**
|
|
* Handle friend invite
|
|
*/
|
|
handleFriendInvite(data) {
|
|
console.log('[AeThex Connect] Friend invite received:', data);
|
|
// Game-specific handling of friend invites
|
|
// Could show custom UI or trigger game's friend system
|
|
}
|
|
|
|
/**
|
|
* Get session duration in seconds
|
|
*/
|
|
getSessionDuration() {
|
|
// This would be calculated based on session start time
|
|
// For now, return 0 as placeholder
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Escape HTML to prevent XSS
|
|
*/
|
|
escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
/**
|
|
* Cleanup and destroy plugin
|
|
*/
|
|
destroy() {
|
|
this.endSession();
|
|
|
|
if (this.overlayElement) {
|
|
this.overlayElement.remove();
|
|
}
|
|
|
|
console.log('[AeThex Connect] Plugin destroyed');
|
|
}
|
|
}
|
|
|
|
// Export for different module systems
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = AeThexConnectPlugin;
|
|
}
|
|
|
|
if (typeof window !== 'undefined') {
|
|
window.AeThexConnectPlugin = AeThexConnectPlugin;
|
|
}
|