MC / templates /settings.html
smodusermc's picture
Create settings.html
2d5e120 verified
Raw History Blame Contribute Delete
16.7 kB
{% extends "base.html" %}
{% block page_title %}Settings{% endblock %}
{% block content %}
<div class="settings-container">
<!-- Server Properties -->
<div class="card">
<div class="card-header">
<h3>Server Properties</h3>
<button class="btn btn-primary" onclick="saveProperties()">Save Changes</button>
</div>
<div class="card-body">
<div class="settings-grid">
<div class="setting-group">
<h4>General</h4>
<div class="setting-item">
<label for="prop-motd">Server MOTD</label>
<input type="text" id="prop-motd" value="{{ properties.get('motd', '') }}">
</div>
<div class="setting-item">
<label for="prop-max-players">Max Players</label>
<input type="number" id="prop-max-players" value="{{ properties.get('max-players', '2') }}" min="1" max="2">
<span class="setting-hint">Limited to 2 for free tier</span>
</div>
<div class="setting-item">
<label for="prop-difficulty">Difficulty</label>
<select id="prop-difficulty">
<option value="peaceful" {{ 'selected' if properties.get('difficulty') == 'peaceful' else '' }}>Peaceful</option>
<option value="easy" {{ 'selected' if properties.get('difficulty') == 'easy' else '' }}>Easy</option>
<option value="normal" {{ 'selected' if properties.get('difficulty') == 'normal' else '' }}>Normal</option>
<option value="hard" {{ 'selected' if properties.get('difficulty') == 'hard' else '' }}>Hard</option>
</select>
</div>
<div class="setting-item">
<label for="prop-gamemode">Default Gamemode</label>
<select id="prop-gamemode">
<option value="survival" {{ 'selected' if properties.get('gamemode') == 'survival' else '' }}>Survival</option>
<option value="creative" {{ 'selected' if properties.get('gamemode') == 'creative' else '' }}>Creative</option>
<option value="adventure" {{ 'selected' if properties.get('gamemode') == 'adventure' else '' }}>Adventure</option>
<option value="spectator" {{ 'selected' if properties.get('gamemode') == 'spectator' else '' }}>Spectator</option>
</select>
</div>
</div>
<div class="setting-group">
<h4>World</h4>
<div class="setting-item">
<label for="prop-level-name">World Name</label>
<input type="text" id="prop-level-name" value="{{ properties.get('level-name', 'world') }}">
</div>
<div class="setting-item">
<label for="prop-view-distance">View Distance</label>
<input type="number" id="prop-view-distance" value="{{ properties.get('view-distance', '10') }}" min="3" max="16">
</div>
<div class="setting-item">
<label for="prop-simulation-distance">Simulation Distance</label>
<input type="number" id="prop-simulation-distance" value="{{ properties.get('simulation-distance', '8') }}" min="3" max="12">
</div>
<div class="setting-item checkbox">
<label>
<input type="checkbox" id="prop-pvp" {{ 'checked' if properties.get('pvp', 'true') == 'true' else '' }}>
Enable PvP
</label>
</div>
<div class="setting-item checkbox">
<label>
<input type="checkbox" id="prop-allow-flight" {{ 'checked' if properties.get('allow-flight', 'false') == 'true' else '' }}>
Allow Flight
</label>
</div>
</div>
<div class="setting-group">
<h4>Security</h4>
<div class="setting-item checkbox">
<label>
<input type="checkbox" id="prop-online-mode" {{ 'checked' if properties.get('online-mode', 'true') == 'true' else '' }}>
Online Mode (Premium only)
</label>
<span class="setting-hint">Disable for cracked clients</span>
</div>
<div class="setting-item checkbox">
<label>
<input type="checkbox" id="prop-white-list" {{ 'checked' if properties.get('white-list', 'false') == 'true' else '' }}>
Enable Whitelist
</label>
</div>
<div class="setting-item checkbox">
<label>
<input type="checkbox" id="prop-enable-command-block" {{ 'checked' if properties.get('enable-command-block', 'true') == 'true' else '' }}>
Enable Command Blocks
</label>
</div>
<div class="setting-item">
<label for="prop-spawn-protection">Spawn Protection Radius</label>
<input type="number" id="prop-spawn-protection" value="{{ properties.get('spawn-protection', '0') }}" min="0" max="50">
</div>
</div>
<div class="setting-group">
<h4>Performance</h4>
<div class="setting-item">
<label for="prop-max-tick-time">Max Tick Time (ms)</label>
<input type="number" id="prop-max-tick-time" value="{{ properties.get('max-tick-time', '60000') }}" min="-1">
<span class="setting-hint">-1 to disable watchdog</span>
</div>
<div class="setting-item">
<label for="prop-network-compression">Network Compression Threshold</label>
<input type="number" id="prop-network-compression" value="{{ properties.get('network-compression-threshold', '256') }}" min="-1">
</div>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="pinger-info">
<div class="info-row">
<span class="info-label">Status:</span>
<span class="info-value">{{ 'Running' if pinger_status.running else 'Stopped' }}</span>
</div>
<div class="info-row">
<span class="info-label">Ping Interval:</span>
<span class="info-value">{{ pinger_status.interval }} seconds</span>
</div>
<div class="info-row">
<span class="info-label">Total Pings:</span>
<span class="info-value">{{ pinger_status.ping_count }}</span>
</div>
<div class="info-row">
<span class="info-label">Last Ping:</span>
<span class="info-value">{{ pinger_status.last_ping or 'Never' }}</span>
</div>
<div class="info-row">
<span class="info-label">Target URL:</span>
<span class="info-value">{{ pinger_status.target_url or 'Auto-detect' }}</span>
</div>
</div>
<div class="pinger-note">
<p><strong>ℹ️ About Keep-Alive:</strong> This service automatically pings your Space
every {{ pinger_status.interval // 60 }} minutes to prevent it from sleeping due to inactivity.
This is essential for maintaining server availability on HuggingFace Spaces free tier.</p>
</div>
</div>
</div>
<!-- Connection Info -->
<div class="card">
<div class="card-header">
<h3>Connection Information</h3>
</div>
<div class="card-body">
<div class="connection-info">
<div class="info-box">
<h4>🌐 How to Connect</h4>
<p>To connect to this Minecraft server, you'll need to use a TCP tunnel service
since HuggingFace Spaces doesn't expose the Minecraft port directly.</p>
<div class="connection-steps">
<h5>Option 1: Using playit.gg (Recommended)</h5>
<ol>
<li>Go to <a href="https://playit.gg" target="_blank">playit.gg</a> and create an account</li>
<li>Download and run the playit agent</li>
<li>Create a Minecraft Java tunnel pointing to <code>localhost:25565</code></li>
<li>Share the generated address with your friend</li>
</ol>
<h5>Option 2: Using ngrok</h5>
<ol>
<li>Install ngrok: <code>npm install -g ngrok</code></li>
<li>Run: <code>ngrok tcp 25565</code></li>
<li>Use the provided address to connect</li>
</ol>
</div>
</div>
<div class="info-box warning">
<h4>⚠️ Important Notes</h4>
<ul>
<li>The server runs on port <strong>25565</strong> internally</li>
<li>You need an external tunnel to make it accessible from the internet</li>
<li>Free tunnel services may have connection limits</li>
<li>Consider setting <strong>Online Mode</strong> to false if using cracked launchers</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Danger Zone -->
<div class="card danger-zone">
<div class="card-header">
<h3>⚠️ Danger Zone</h3>
</div>
<div class="card-body">
<div class="danger-actions">
<div class="danger-item">
<div class="danger-info">
<h4>Reset Server</h4>
<p>Delete all world data and start fresh. This cannot be undone!</p>
</div>
<button class="btn btn-danger" onclick="resetServer()">Reset Server</button>
</div>
<div class="danger-item">
<div class="danger-info">
<h4>Delete All Mods</h4>
<p>Remove all installed mods from the server.</p>
</div>
<button class="btn btn-danger" onclick="deleteAllMods()">Delete All Mods</button>
</div>
<div class="danger-item">
<div class="danger-info">
<h4>Force Stop Server</h4>
<p>Forcefully terminate the server process. May cause data loss!</p>
</div>
<button class="btn btn-danger" onclick="forceStop()">Force Stop</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
async function saveProperties() {
const properties = {
'motd': document.getElementById('prop-motd').value,
'max-players': document.getElementById('prop-max-players').value,
'difficulty': document.getElementById('prop-difficulty').value,
'gamemode': document.getElementById('prop-gamemode').value,
'level-name': document.getElementById('prop-level-name').value,
'view-distance': document.getElementById('prop-view-distance').value,
'simulation-distance': document.getElementById('prop-simulation-distance').value,
'pvp': document.getElementById('prop-pvp').checked ? 'true' : 'false',
'allow-flight': document.getElementById('prop-allow-flight').checked ? 'true' : 'false',
'online-mode': document.getElementById('prop-online-mode').checked ? 'true' : 'false',
'white-list': document.getElementById('prop-white-list').checked ? 'true' : 'false',
'enable-command-block': document.getElementById('prop-enable-command-block').checked ? 'true' : 'false',
'spawn-protection': document.getElementById('prop-spawn-protection').value,
'max-tick-time': document.getElementById('prop-max-tick-time').value,
'network-compression-threshold': document.getElementById('prop-network-compression').value
};
try {
const response = await fetch('/api/settings/properties', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(properties)
});
const data = await response.json();
showToast(data.message, data.success ? 'success' : 'error');
if (data.success) {
showToast('Restart server to apply changes', 'info');
}
} catch (error) {
showToast('Failed to save properties', 'error');
}
}
async function resetServer() {
if (!confirm('⚠️ WARNING: This will delete ALL world data!\n\nAre you absolutely sure?')) return;
if (!confirm('This is your LAST CHANCE to cancel.\n\nType "RESET" in the next prompt to confirm.')) return;
const confirmation = prompt('Type "RESET" to confirm:');
if (confirmation !== 'RESET') {
showToast('Reset cancelled', 'info');
return;
}
try {
// Stop server first
await fetch('/api/server/stop', {method: 'POST'});
// Delete world folder
await fetch('/api/files/delete', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({path: 'world'})
});
showToast('Server reset complete. Start the server to generate a new world.', 'success');
} catch (error) {
showToast('Reset failed: ' + error.message, 'error');
}
}
async function deleteAllMods() {
if (!confirm('Delete ALL installed mods?')) return;
try {
const response = await fetch('/api/mods');
const mods = await response.json();
for (const mod of mods) {
await fetch('/api/mods/delete', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({filename: mod.filename})
});
}
showToast('All mods deleted', 'success');
} catch (error) {
showToast('Failed to delete mods', 'error');
}
}
async function forceStop() {
if (!confirm('Force stop the server? This may cause data loss!')) return;
try {
const response = await fetch('/api/server/stop', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({force: true})
});
const data = await response.json();
showToast(data.message, data.success ? 'success' : 'error');
} catch (error) {
showToast('Force stop failed', 'error');
}
}
</script>
{% endblock %}