mcbot / public /index.html
wpms's picture
Upload index.html
628782c verified
Raw History Blame Contribute Delete
37.4 kB
<!DOCTYPE html>
<html lang="si">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minecraft Bot Controller</title>
<script src="/socket.io/socket.io.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
color: #e2e8f0;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
padding: 30px 0;
margin-bottom: 20px;
}
.header h1 {
font-size: 2.5rem;
background: linear-gradient(135deg, #60a5fa, #a78bfa);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 10px;
}
.header p {
color: #94a3b8;
font-size: 1.1rem;
}
.panel {
background: rgba(30, 41, 59, 0.8);
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 16px;
padding: 24px;
margin-bottom: 20px;
}
.connection-form {
display: grid;
grid-template-columns: 1fr 1fr 120px 150px;
gap: 15px;
align-items: end;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #94a3b8;
font-size: 0.9rem;
font-weight: 500;
}
.form-group input {
width: 100%;
padding: 12px 16px;
background: #0f172a;
border: 2px solid #334155;
border-radius: 10px;
color: #f1f5f9;
font-size: 1rem;
transition: all 0.3s;
}
.form-group input:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 10px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn-primary {
background: linear-gradient(135deg, #3b82f6, #2563eb);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(59, 130, 246, 0.4);
}
.btn-danger {
background: linear-gradient(135deg, #ef4444, #dc2626);
color: white;
}
.btn-danger:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(239, 68, 68, 0.4);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none !important;
}
.status-bar {
display: flex;
align-items: center;
justify-content: space-between;
background: rgba(15, 23, 42, 0.6);
padding: 16px 20px;
border-radius: 12px;
margin-top: 20px;
}
.status-indicator {
display: flex;
align-items: center;
gap: 10px;
}
.status-dot {
width: 12px;
height: 12px;
border-radius: 50%;
position: relative;
}
.status-dot::after {
content: '';
position: absolute;
inset: -4px;
border-radius: 50%;
opacity: 0.4;
animation: pulse 2s infinite;
}
.status-online {
background: #22c55e;
box-shadow: 0 0 10px #22c55e;
}
.status-online::after {
background: #22c55e;
}
.status-offline {
background: #ef4444;
box-shadow: 0 0 10px #ef4444;
}
.status-offline::after {
background: #ef4444;
animation: none;
}
.status-connecting {
background: #eab308;
box-shadow: 0 0 10px #eab308;
animation: pulse 1s infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.2); opacity: 0.7; }
}
.status-text {
font-weight: 600;
font-size: 1rem;
}
.status-online + .status-text { color: #4ade80; }
.status-offline + .status-text { color: #f87171; }
.status-connecting + .status-text { color: #facc15; }
.bot-info {
display: flex;
align-items: center;
gap: 15px;
}
.bot-avatar {
width: 40px;
height: 40px;
border-radius: 8px;
image-rendering: pixelated;
border: 2px solid #3b82f6;
}
.bot-details {
text-align: right;
}
.bot-name {
font-weight: 700;
color: #60a5fa;
}
.server-address {
font-size: 0.85rem;
color: #94a3b8;
}
.main-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 20px;
}
.chat-section {
display: flex;
flex-direction: column;
height: 600px;
}
.chat-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.chat-header h2 {
display: flex;
align-items: center;
gap: 10px;
font-size: 1.3rem;
}
.badge {
background: #ef4444;
color: white;
padding: 2px 8px;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 700;
}
.chat-controls {
display: flex;
gap: 8px;
}
.chat-controls button {
padding: 6px 12px;
background: #334155;
border: none;
border-radius: 6px;
color: #cbd5e1;
font-size: 0.85rem;
cursor: pointer;
transition: all 0.3s;
}
.chat-controls button:hover {
background: #475569;
}
.chat-messages {
flex: 1;
overflow-y: auto;
background: rgba(15, 23, 42, 0.6);
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
scrollbar-width: thin;
scrollbar-color: #334155 transparent;
}
.chat-messages::-webkit-scrollbar {
width: 6px;
}
.chat-messages::-webkit-scrollbar-track {
background: transparent;
}
.chat-messages::-webkit-scrollbar-thumb {
background: #334155;
border-radius: 3px;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #64748b;
}
.empty-state .icon {
font-size: 3rem;
margin-bottom: 16px;
}
.message {
display: flex;
gap: 12px;
padding: 10px;
border-radius: 10px;
margin-bottom: 8px;
animation: fadeIn 0.3s ease;
transition: background 0.2s;
}
.message:hover {
background: rgba(255,255,255,0.03);
}
.message.bot-message {
background: rgba(59, 130, 246, 0.1);
border: 1px solid rgba(59, 130, 246, 0.2);
}
.message.system-message {
justify-content: center;
text-align: center;
}
.message.system-message .system-text {
background: rgba(255,255,255,0.05);
padding: 6px 16px;
border-radius: 20px;
font-size: 0.85rem;
color: #94a3b8;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.message-avatar {
width: 36px;
height: 36px;
border-radius: 6px;
image-rendering: pixelated;
flex-shrink: 0;
}
.message-content {
flex: 1;
min-width: 0;
}
.message-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 4px;
}
.message-author {
font-weight: 700;
font-size: 0.9rem;
}
.message-author.bot { color: #60a5fa; }
.message-author.player { color: #4ade80; }
.message-author.server { color: #fbbf24; }
.message-time {
font-size: 0.75rem;
color: #64748b;
}
.message-text {
color: #e2e8f0;
line-height: 1.5;
word-break: break-word;
}
.chat-input-area {
display: flex;
gap: 12px;
}
.chat-input {
flex: 1;
padding: 14px 18px;
background: #0f172a;
border: 2px solid #334155;
border-radius: 12px;
color: #f1f5f9;
font-size: 1rem;
transition: all 0.3s;
}
.chat-input:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
}
.chat-input:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.sidebar {
display: flex;
flex-direction: column;
gap: 20px;
}
.players-list {
max-height: 300px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #334155 transparent;
}
.players-list::-webkit-scrollbar {
width: 6px;
}
.players-list::-webkit-scrollbar-thumb {
background: #334155;
border-radius: 3px;
}
.player-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px;
border-radius: 10px;
transition: all 0.3s;
margin-bottom: 6px;
}
.player-item:hover {
background: rgba(255,255,255,0.05);
}
.player-avatar {
width: 32px;
height: 32px;
border-radius: 6px;
image-rendering: pixelated;
}
.player-info {
flex: 1;
min-width: 0;
}
.player-name {
font-weight: 600;
font-size: 0.9rem;
color: #e2e8f0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.player-ping {
font-size: 0.75rem;
color: #64748b;
}
.player-status {
width: 8px;
height: 8px;
border-radius: 50%;
background: #22c55e;
box-shadow: 0 0 6px #22c55e;
}
.actions-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.action-btn {
padding: 10px;
background: #334155;
border: none;
border-radius: 8px;
color: #cbd5e1;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
}
.action-btn:hover {
background: #475569;
transform: translateY(-2px);
}
.action-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.quick-commands {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.quick-btn {
padding: 8px 14px;
background: #334155;
border: none;
border-radius: 8px;
color: #cbd5e1;
font-size: 0.85rem;
cursor: pointer;
transition: all 0.3s;
}
.quick-btn:hover {
background: #475569;
transform: translateY(-1px);
}
.console-section {
margin-top: 20px;
}
.console-log {
max-height: 200px;
overflow-y: auto;
background: rgba(15, 23, 42, 0.8);
border-radius: 12px;
padding: 16px;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
scrollbar-width: thin;
scrollbar-color: #334155 transparent;
}
.console-log::-webkit-scrollbar {
width: 6px;
}
.console-log::-webkit-scrollbar-thumb {
background: #334155;
border-radius: 3px;
}
.log-entry {
padding: 4px 0;
border-bottom: 1px solid rgba(255,255,255,0.05);
}
.log-time {
color: #64748b;
margin-right: 8px;
}
.log-info { color: #60a5fa; }
.log-success { color: #4ade80; }
.log-error { color: #f87171; }
.log-chat { color: #c084fc; }
.log-join { color: #86efac; }
.log-leave { color: #fca5a5; }
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-top: 16px;
}
.stat-card {
background: rgba(15, 23, 42, 0.6);
padding: 16px;
border-radius: 12px;
text-align: center;
}
.stat-value {
font-size: 1.5rem;
font-weight: 700;
color: #60a5fa;
}
.stat-label {
font-size: 0.85rem;
color: #94a3b8;
margin-top: 4px;
}
.hidden { display: none !important; }
@media (max-width: 900px) {
.main-grid {
grid-template-columns: 1fr;
}
.connection-form {
grid-template-columns: 1fr;
}
.sidebar {
order: -1;
}
}
.toast {
position: fixed;
bottom: 20px;
right: 20px;
padding: 16px 24px;
border-radius: 12px;
color: white;
font-weight: 600;
animation: slideIn 0.3s ease;
z-index: 1000;
}
.toast-success { background: #22c55e; }
.toast-error { background: #ef4444; }
.toast-info { background: #3b82f6; }
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🎮 Minecraft Bot Controller</h1>
<p>Connect your bot to any Minecraft server and control it from the web</p>
</div>
<!-- Connection Panel -->
<div class="panel">
<div class="connection-form">
<div class="form-group">
<label>🧑 Player Name</label>
<input type="text" id="playerName" placeholder="BotName123" value="HFBot_4220">
</div>
<div class="form-group">
<label>🌐 Server IP</label>
<input type="text" id="serverIp" placeholder="play.example.com" value="play.hypixel.net">
</div>
<div class="form-group">
<label>🔌 Port</label>
<input type="number" id="serverPort" placeholder="25565" value="25565">
</div>
<button id="connectBtn" class="btn btn-primary" onclick="toggleConnection()">
<span id="btnIcon">🔗</span>
<span id="btnText">Connect Bot</span>
</button>
</div>
<div class="status-bar">
<div class="status-indicator">
<div id="statusDot" class="status-dot status-offline"></div>
<span id="statusText" class="status-text">Disconnected</span>
</div>
<div id="botInfo" class="bot-info hidden">
<img id="botAvatar" src="" alt="" class="bot-avatar">
<div class="bot-details">
<div id="botName" class="bot-name"></div>
<div id="serverAddr" class="server-address"></div>
</div>
</div>
</div>
</div>
<!-- Main Grid -->
<div class="main-grid">
<!-- Chat Section -->
<div class="panel chat-section">
<div class="chat-header">
<h2>
💬 Server Chat
<span id="chatBadge" class="badge hidden">0</span>
</h2>
<div class="chat-controls">
<button onclick="clearChat()">🧹 Clear</button>
<button onclick="toggleAutoScroll()" id="autoScrollBtn">📜 Auto</button>
</div>
</div>
<div id="chatMessages" class="chat-messages">
<div class="empty-state">
<div class="icon">📡</div>
<p>Connect to a server to start chatting</p>
</div>
</div>
<div class="chat-input-area">
<input type="text" id="chatInput" class="chat-input" placeholder="Type a message..."
onkeypress="if(event.key==='Enter') sendChat()" disabled>
<button id="sendBtn" class="btn btn-primary" onclick="sendChat()" disabled>
📤 Send
</button>
</div>
</div>
<!-- Sidebar -->
<div class="sidebar">
<!-- Players Online -->
<div class="panel">
<h2 style="display:flex;align-items:center;gap:10px;margin-bottom:16px;">
👥 Players Online
<span id="onlineCount" class="badge" style="background:#22c55e;">0</span>
</h2>
<div id="playersList" class="players-list">
<p style="text-align:center;color:#64748b;padding:20px;">Not connected</p>
</div>
</div>
<!-- Bot Stats -->
<div class="panel">
<h2 style="margin-bottom:16px;">📊 Bot Stats</h2>
<div class="stats-grid">
<div class="stat-card">
<div id="healthStat" class="stat-value">❤️ 20</div>
<div class="stat-label">Health</div>
</div>
<div class="stat-card">
<div id="foodStat" class="stat-value">🍖 20</div>
<div class="stat-label">Food</div>
</div>
</div>
</div>
<!-- Bot Actions -->
<div class="panel">
<h2 style="margin-bottom:16px;">🤖 Bot Actions</h2>
<div class="actions-grid">
<button class="action-btn" onclick="botAction('jump')" id="jumpBtn" disabled>
⬆️ Jump
</button>
<button class="action-btn" onclick="botAction('sneak')" id="sneakBtn" disabled>
👇 Sneak
</button>
<button class="action-btn" onclick="botAction('move', 'forward')" id="forwardBtn" disabled>
⬆️ Forward
</button>
<button class="action-btn" onclick="botAction('move', 'back')" id="backBtn" disabled>
⬇️ Back
</button>
<button class="action-btn" onclick="sendCommand('/list')" id="listBtn" disabled>
📋 List
</button>
<button class="action-btn" onclick="sendCommand('/help')" id="helpBtn" disabled>
❓ Help
</button>
</div>
</div>
</div>
</div>
<!-- Quick Commands -->
<div class="panel">
<h2 style="margin-bottom:16px;">⚡ Quick Chat</h2>
<div class="quick-commands">
<button class="quick-btn" onclick="quickChat('Hello everyone! 👋')">👋 Hello</button>
<button class="quick-btn" onclick="quickChat('Good game! 🎮')">🎮 GG</button>
<button class="quick-btn" onclick="quickChat('Anyone need help? 🆘')">🆘 Help?</button>
<button class="quick-btn" onclick="quickChat('Nice build! 🏗️')">🏗️ Nice</button>
<button class="quick-btn" onclick="quickChat('BRB ⏸️')">⏸️ BRB</button>
<button class="quick-btn" onclick="quickChat('Back! ▶️')">▶️ Back</button>
<button class="quick-btn" onclick="quickChat('Anyone want to team? 🤝')">🤝 Team?</button>
<button class="quick-btn" onclick="quickChat('Lag? 🐌')">🐌 Lag?</button>
</div>
</div>
<!-- Console Log -->
<div class="panel console-section">
<h2 style="margin-bottom:16px;">📊 Console</h2>
<div id="consoleLog" class="console-log">
<div class="log-entry">
<span class="log-time">[System]</span>
<span class="log-info">Minecraft Bot Controller initialized</span>
</div>
</div>
</div>
</div>
<script>
// Socket.IO Connection
const socket = io();
// State
let isConnected = false;
let autoScroll = true;
let unreadCount = 0;
let currentUsername = '';
// DOM Elements
const connectBtn = document.getElementById('connectBtn');
const btnText = document.getElementById('btnText');
const btnIcon = document.getElementById('btnIcon');
const statusDot = document.getElementById('statusDot');
const statusText = document.getElementById('statusText');
const chatMessages = document.getElementById('chatMessages');
const chatInput = document.getElementById('chatInput');
const sendBtn = document.getElementById('sendBtn');
const playersList = document.getElementById('playersList');
const consoleLog = document.getElementById('consoleLog');
const botInfo = document.getElementById('botInfo');
const botAvatar = document.getElementById('botAvatar');
const botName = document.getElementById('botName');
const serverAddr = document.getElementById('serverAddr');
const onlineCount = document.getElementById('onlineCount');
const chatBadge = document.getElementById('chatBadge');
const healthStat = document.getElementById('healthStat');
const foodStat = document.getElementById('foodStat');
// Action buttons
const actionBtns = ['jumpBtn', 'sneakBtn', 'forwardBtn', 'backBtn', 'listBtn', 'helpBtn'];
// Socket Events
socket.on('connect', () => {
log('Connected to server', 'success');
});
socket.on('disconnect', () => {
log('Disconnected from server', 'error');
handleDisconnect();
});
socket.on('bot-status', (data) => {
updateStatus('online', 'Online');
isConnected = true;
currentUsername = data.username;
btnText.textContent = 'Disconnect';
btnIcon.textContent = '🔌';
connectBtn.classList.remove('btn-primary');
connectBtn.classList.add('btn-danger');
chatInput.disabled = false;
sendBtn.disabled = false;
actionBtns.forEach(id => document.getElementById(id).disabled = false);
botInfo.classList.remove('hidden');
botAvatar.src = `https://mc-heads.net/avatar/${data.username}/40`;
botName.textContent = data.username;
serverAddr.textContent = `${data.host}:${data.port}`;
chatMessages.innerHTML = '';
addSystemMessage('Connected to server!');
showToast('Bot connected successfully!', 'success');
});
socket.on('bot-spawned', (data) => {
log('Bot spawned in world', 'success');
if (data.health) healthStat.textContent = `❤️ ${Math.round(data.health)}`;
if (data.food) foodStat.textContent = `🍖 ${Math.round(data.food)}`;
});
socket.on('chat-message', (data) => {
addChatMessage(data.username, data.message, false, data.timestamp);
if (!document.hasFocus()) {
unreadCount++;
updateBadge();
}
});
socket.on('chat-sent', (data) => {
addChatMessage(data.username, data.message, true, data.timestamp);
});
socket.on('system-message', (data) => {
addSystemMessage(data.message);
});
socket.on('player-joined', (data) => {
addSystemMessage(`✅ ${data.username} joined the game`);
log(`${data.username} joined`, 'join');
});
socket.on('player-left', (data) => {
addSystemMessage(`❌ ${data.username} left the game`);
log(`${data.username} left`, 'leave');
});
socket.on('players-list', (players) => {
updatePlayersList(players);
});
socket.on('bot-health', (data) => {
healthStat.textContent = `❤️ ${Math.round(data.health)}`;
foodStat.textContent = `🍖 ${Math.round(data.food)}`;
});
socket.on('bot-death', (data) => {
addSystemMessage('💀 Bot died!');
log('Bot died', 'error');
});
socket.on('bot-kicked', (data) => {
addSystemMessage(`🚫 Kicked: ${data.reason}`);
log(`Kicked: ${data.reason}`, 'error');
handleDisconnect();
});
socket.on('bot-disconnected', (data) => {
addSystemMessage(`🔌 Disconnected: ${data.reason}`);
log(`Disconnected: ${data.reason}`, 'error');
handleDisconnect();
});
socket.on('bot-error', (data) => {
log(`Error: ${data.message}`, 'error');
showToast(data.message, 'error');
});
socket.on('error', (data) => {
showToast(data.message, 'error');
updateStatus('offline', 'Connection Failed');
resetButton();
});
// Functions
function toggleConnection() {
if (isConnected) {
disconnectBot();
} else {
connectBot();
}
}
function connectBot() {
const username = document.getElementById('playerName').value.trim();
const host = document.getElementById('serverIp').value.trim();
const port = document.getElementById('serverPort').value || 25565;
if (!username || !host) {
showToast('Please enter username and server IP', 'error');
return;
}
updateStatus('connecting', 'Connecting...');
btnText.textContent = 'Connecting...';
btnIcon.textContent = '⏳';
connectBtn.disabled = true;
log(`Connecting to ${host}:${port} as ${username}...`, 'info');
socket.emit('connect-bot', { username, host, port });
// Timeout for connection
setTimeout(() => {
if (!isConnected && btnText.textContent === 'Connecting...') {
updateStatus('offline', 'Connection Timeout');
resetButton();
showToast('Connection timeout. Check server details.', 'error');
}
}, 30000);
}
function disconnectBot() {
socket.emit('disconnect-bot');
log('Disconnecting...', 'info');
}
function handleDisconnect() {
isConnected = false;
currentUsername = '';
updateStatus('offline', 'Disconnected');
resetButton();
chatInput.disabled = true;
sendBtn.disabled = true;
actionBtns.forEach(id => document.getElementById(id).disabled = true);
botInfo.classList.add('hidden');
playersList.innerHTML = '<p style="text-align:center;color:#64748b;padding:20px;">Not connected</p>';
onlineCount.textContent = '0';
healthStat.textContent = '❤️ 20';
foodStat.textContent = '🍖 20';
addSystemMessage('Disconnected from server');
}
function resetButton() {
connectBtn.disabled = false;
btnText.textContent = 'Connect Bot';
btnIcon.textContent = '🔗';
connectBtn.classList.remove('btn-danger');
connectBtn.classList.add('btn-primary');
}
function updateStatus(status, text) {
statusDot.className = 'status-dot';
if (status === 'online') {
statusDot.classList.add('status-online');
statusText.className = 'status-text';
statusText.style.color = '#4ade80';
} else if (status === 'offline') {
statusDot.classList.add('status-offline');
statusText.className = 'status-text';
statusText.style.color = '#f87171';
} else if (status === 'connecting') {
statusDot.classList.add('status-connecting');
statusText.className = 'status-text';
statusText.style.color = '#facc15';
}
statusText.textContent = text;
}
function addChatMessage(username, message, isBot, timestamp) {
const div = document.createElement('div');
div.className = 'message' + (isBot ? ' bot-message' : '');
const time = timestamp ? new Date(timestamp).toLocaleTimeString() : new Date().toLocaleTimeString();
const avatar = `https://mc-heads.net/avatar/${username}/36`;
const authorClass = isBot ? 'bot' : (username === 'Server' ? 'server' : 'player');
div.innerHTML = `
<img src="${avatar}" alt="${username}" class="message-avatar"
onerror="this.src='https://mc-heads.net/avatar/Steve/36'">
<div class="message-content">
<div class="message-header">
<span class="message-author ${authorClass}">${escapeHtml(username)}</span>
<span class="message-time">${time}</span>
</div>
<p class="message-text">${escapeHtml(message)}</p>
</div>
`;
chatMessages.appendChild(div);
if (autoScroll) {
chatMessages.scrollTop = chatMessages.scrollHeight;
}
}
function addSystemMessage(text) {
const div = document.createElement('div');
div.className = 'message system-message';
div.innerHTML = `<span class="system-text">${escapeHtml(text)}</span>`;
chatMessages.appendChild(div);
if (autoScroll) {
chatMessages.scrollTop = chatMessages.scrollHeight;
}
}
function sendChat() {
const message = chatInput.value.trim();
if (!message || !isConnected) return;
socket.emit('send-chat', message);
chatInput.value = '';
chatInput.focus();
}
function quickChat(message) {
if (!isConnected) {
showToast('Connect to a server first', 'error');
return;
}
socket.emit('send-chat', message);
}
function sendCommand(command) {
if (!isConnected) {
showToast('Connect to a server first', 'error');
return;
}
socket.emit('send-command', command);
log(`Command: ${command}`, 'info');
}
function botAction(type, direction) {
if (!isConnected) return;
const action = { type };
if (direction) action.direction = direction;
socket.emit('bot-action', action);
log(`Action: ${type}${direction ? ' ' + direction : ''}`, 'info');
}
function updatePlayersList(players) {
onlineCount.textContent = players.length;
if (players.length === 0) {
playersList.innerHTML = '<p style="text-align:center;color:#64748b;padding:20px;">No players online</p>';
return;
}
playersList.innerHTML = players.map(p => `
<div class="player-item">
<img src="https://mc-heads.net/avatar/${p.username}/32"
class="player-avatar"
onerror="this.src='https://mc-heads.net/avatar/Steve/32'">
<div class="player-info">
<div class="player-name">${escapeHtml(p.username)}</div>
<div class="player-ping">${p.ping}ms</div>
</div>
<div class="player-status"></div>
</div>
`).join('');
}
function clearChat() {
chatMessages.innerHTML = `
<div class="empty-state">
<div class="icon">🧹</div>
<p>Chat cleared</p>
</div>
`;
unreadCount = 0;
updateBadge();
}
function toggleAutoScroll() {
autoScroll = !autoScroll;
document.getElementById('autoScrollBtn').textContent = autoScroll ? '📜 Auto' : '📜 Manual';
if (autoScroll) {
chatMessages.scrollTop = chatMessages.scrollHeight;
}
}
function updateBadge() {
if (unreadCount > 0) {
chatBadge.textContent = unreadCount;
chatBadge.classList.remove('hidden');
} else {
chatBadge.classList.add('hidden');
}
}
function log(message, type = 'info') {
const entry = document.createElement('div');
entry.className = 'log-entry';
const time = new Date().toLocaleTimeString();
entry.innerHTML = `
<span class="log-time">[${time}]</span>
<span class="log-${type}">${escapeHtml(message)}</span>
`;
consoleLog.appendChild(entry);
consoleLog.scrollTop = consoleLog.scrollHeight;
}
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateX(100%)';
setTimeout(() => toast.remove(), 300);
}, 3000);
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Window focus handler
window.addEventListener('focus', () => {
unreadCount = 0;
updateBadge();
});
// Initialize
log('System ready. Enter server details and click Connect.', 'info');
</script>
</body>
</html>