|
|
import asyncio |
|
|
import websockets |
|
|
import os |
|
|
from http import HTTPStatus |
|
|
|
|
|
|
|
|
HTTP_PORT = 7860 |
|
|
WEBSOCKET_PORT = 8765 |
|
|
MINECRAFT_RAM = "12G" |
|
|
PLAYIT_SECRET = os.environ.get('PLAYIT_SECRET') |
|
|
|
|
|
|
|
|
connected_clients = set() |
|
|
|
|
|
async def start_subprocess(command): |
|
|
"""Starts a subprocess and returns the process object.""" |
|
|
print(f"Starting subprocess: {' '.join(command)}") |
|
|
process = await asyncio.create_subprocess_exec( |
|
|
*command, |
|
|
stdout=asyncio.subprocess.PIPE, |
|
|
stdin=asyncio.subprocess.PIPE, |
|
|
stderr=asyncio.subprocess.PIPE |
|
|
) |
|
|
return process |
|
|
|
|
|
async def broadcast(message): |
|
|
"""Sends a message to all connected clients.""" |
|
|
if connected_clients: |
|
|
await asyncio.wait([client.send(message) for client in connected_clients]) |
|
|
|
|
|
async def read_stream(stream, prefix=""): |
|
|
"""Reads lines from a stream and broadcasts them.""" |
|
|
while True: |
|
|
line = await stream.readline() |
|
|
if not line: |
|
|
break |
|
|
output = line.decode('utf-8', errors='replace').strip() |
|
|
print(f"{prefix}{output}") |
|
|
await broadcast(f"{prefix}{output}") |
|
|
|
|
|
async def websocket_handler(websocket, path): |
|
|
"""Handles WebSocket connections.""" |
|
|
global mc_process |
|
|
print("Client connected to WebSocket.") |
|
|
connected_clients.add(websocket) |
|
|
try: |
|
|
|
|
|
async for message in websocket: |
|
|
print(f"Received command: {message}") |
|
|
if mc_process and not mc_process.stdin.is_closing(): |
|
|
|
|
|
command = message.strip() + '\n' |
|
|
mc_process.stdin.write(command.encode('utf-8')) |
|
|
await mc_process.stdin.drain() |
|
|
else: |
|
|
print("Minecraft process is not running or stdin is closed.") |
|
|
await websocket.send("Error: Minecraft server is not running.") |
|
|
except websockets.exceptions.ConnectionClosed: |
|
|
print("Client disconnected.") |
|
|
finally: |
|
|
connected_clients.remove(websocket) |
|
|
|
|
|
async def http_handler(reader, writer): |
|
|
"""Handles basic HTTP requests to serve the HTML file.""" |
|
|
try: |
|
|
request_line = await reader.readline() |
|
|
if not request_line: |
|
|
return |
|
|
|
|
|
|
|
|
method, path, _ = request_line.decode().split(' ') |
|
|
|
|
|
if method == "GET" and path == "/": |
|
|
writer.write(b"HTTP/1.1 200 OK\r\n") |
|
|
writer.write(b"Content-Type: text/html\r\n") |
|
|
writer.write(b"\r\n") |
|
|
with open("index.html", "rb") as f: |
|
|
writer.write(f.read()) |
|
|
else: |
|
|
writer.write(b"HTTP/1.1 404 Not Found\r\n\r\n") |
|
|
writer.write(b"Not Found") |
|
|
|
|
|
await writer.drain() |
|
|
except Exception as e: |
|
|
print(f"HTTP Server Error: {e}") |
|
|
finally: |
|
|
writer.close() |
|
|
|
|
|
async def main(): |
|
|
global mc_process |
|
|
|
|
|
|
|
|
if PLAYIT_SECRET: |
|
|
playit_command = ['playit', '--secret', PLAYIT_SECRET] |
|
|
playit_process = await start_subprocess(playit_command) |
|
|
|
|
|
asyncio.create_task(read_stream(playit_process.stdout, "[playit] ")) |
|
|
asyncio.create_task(read_stream(playit_process.stderr, "[playit/err] ")) |
|
|
else: |
|
|
print("Warning: PLAYIT_SECRET not set. Tunnel will not be started.") |
|
|
|
|
|
|
|
|
mc_command = [ |
|
|
'java', f'-Xmx{MINECRAFT_RAM}', '-Xms1G', |
|
|
'-jar', 'server.jar', 'nogui' |
|
|
] |
|
|
mc_process = await start_subprocess(mc_command) |
|
|
|
|
|
|
|
|
asyncio.create_task(read_stream(mc_process.stdout, "")) |
|
|
asyncio.create_task(read_stream(mc_process.stderr, "[mc/err] ")) |
|
|
|
|
|
|
|
|
websocket_server = await websockets.serve(websocket_handler, "0.0.0.0", WEBSOCKET_PORT) |
|
|
print(f"WebSocket server started on port {WEBSOCKET_PORT}") |
|
|
|
|
|
http_server = await asyncio.start_server(http_handler, "0.0.0.0", HTTP_PORT) |
|
|
print(f"HTTP server started on port {HTTP_PORT}") |
|
|
|
|
|
|
|
|
await asyncio.gather( |
|
|
websocket_server.wait_closed(), |
|
|
http_server.wait_closed() |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
try: |
|
|
asyncio.run(main()) |
|
|
except KeyboardInterrupt: |
|
|
print("Server shutting down.") |