import os import sys import subprocess import json import threading import time import queue import signal from flask import Flask, render_template_string, request, jsonify from flask_sock import Sock import psutil app = Flask(__name__) sock = Sock(app) mc_process = None output_queue = queue.Queue() command_queue = queue.Queue() server_ready = threading.Event() def start_minecraft(): global mc_process mc_dir = "/app/mcserver" print("[MC] Starting Minecraft server...", flush=True) mc_process = subprocess.Popen( ["java", "-Xms1G", "-Xmx14G", "-XX:+UseG1GC", "-XX:+ParallelRefProcEnabled", "-XX:MaxGCPauseMillis=200", "-jar", "server.jar", "nogui"], cwd=mc_dir, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, preexec_fn=os.setsid ) print(f"[MC] Minecraft PID: {mc_process.pid}", flush=True) def read_output(): try: for line in mc_process.stdout: output_queue.put(line) if "Done" in line and "For help, type" in line: server_ready.set() print("[MC] Server is ready!", flush=True) except Exception as e: print(f"[MC] Output error: {e}", flush=True) threading.Thread(target=read_output, daemon=True).start() def process_commands(): while True: try: cmd = command_queue.get(timeout=1) if mc_process and mc_process.poll() is None: try: mc_process.stdin.write(cmd + "\n") mc_process.stdin.flush() print(f"[MC] Command: {cmd}", flush=True) except Exception as e: print(f"[MC] Command error: {e}", flush=True) except queue.Empty: continue except Exception as e: print(f"[MC] Processor error: {e}", flush=True) break threading.Thread(target=process_commands, daemon=True).start() HTML_TEMPLATE = """ Minecraft Server - Web Console
Connecting...
Minecraft Server Offline

Loading files...

""" @app.route('/') def index(): return render_template_string(HTML_TEMPLATE) @app.route('/health') def health(): return {'status': 'running', 'minecraft': mc_process is not None and mc_process.poll() is None} @app.route('/api/files') def get_files(): path = request.args.get('path', '/app/mcserver') if not path.startswith('/app/mcserver'): path = '/app/mcserver' try: files = [] for item in sorted(os.listdir(path)): item_path = os.path.join(path, item) is_dir = os.path.isdir(item_path) size = os.path.getsize(item_path) if not is_dir else 0 files.append({'name': item, 'is_dir': is_dir, 'size': size, 'path': item_path}) return jsonify(files) except Exception as e: return jsonify({'error': str(e)}), 500 @sock.route('/ws') def websocket(ws): print("[WS] Client connected", flush=True) history = [] temp_list = [] while not output_queue.empty() and len(temp_list) < 100: try: line = output_queue.get_nowait() temp_list.append(line) except: break for line in temp_list: output_queue.put(line) history.append(line) for line in history[-50:]: try: ws.send(json.dumps({'type': 'output', 'data': line})) except: break def send_stats(): while True: try: cpu = psutil.cpu_percent(interval=0.5) memory = psutil.virtual_memory() running = mc_process is not None and mc_process.poll() is None disk_size = 0 try: for dirpath, dirnames, filenames in os.walk('/app/mcserver'): for f in filenames: fp = os.path.join(dirpath, f) if os.path.exists(fp): disk_size += os.path.getsize(fp) except: pass ws.send(json.dumps({ 'type': 'stats', 'cpu': cpu, 'memory': memory.percent, 'memoryTotal': memory.total, 'running': running, 'players': 0, 'disk': disk_size })) except Exception as e: print(f"[WS] Stats error: {e}", flush=True) break time.sleep(2) threading.Thread(target=send_stats, daemon=True).start() def send_output(): while True: try: line = output_queue.get(timeout=1) try: ws.send(json.dumps({'type': 'output', 'data': line})) except: break except queue.Empty: continue except: break threading.Thread(target=send_output, daemon=True).start() while True: try: data = ws.receive(timeout=5) if data: msg = json.loads(data) if msg.get('type') == 'command': cmd = msg.get('data', '').strip() if cmd: print(f"[WS] Command: {cmd}", flush=True) command_queue.put(cmd) except Exception as e: if "Connection closed" in str(e): break continue print("[WS] Client disconnected", flush=True) def cleanup(signum=None, frame=None): print("\n[MAIN] Shutting down...", flush=True) if mc_process: try: mc_process.stdin.write("stop\n") mc_process.stdin.flush() mc_process.wait(timeout=30) except: try: os.killpg(os.getpgid(mc_process.pid), signal.SIGTERM) except: pass sys.exit(0) signal.signal(signal.SIGTERM, cleanup) signal.signal(signal.SIGINT, cleanup) if __name__ == '__main__': print("[MAIN] Starting web server...", flush=True) mc_thread = threading.Thread(target=start_minecraft, daemon=True) mc_thread.start() from werkzeug.serving import run_simple run_simple('0.0.0.0', 7860, app, threaded=True, use_reloader=False)