File size: 4,582 Bytes
1645c43 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
import asyncio
import websockets
import os
from http import HTTPStatus
# --- Configuration ---
HTTP_PORT = 7860
WEBSOCKET_PORT = 8765
MINECRAFT_RAM = "12G"
PLAYIT_SECRET = os.environ.get('PLAYIT_SECRET')
# A set to keep track of all connected WebSocket clients
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:
# Keep the connection open and listen for commands from the client
async for message in websocket:
print(f"Received command: {message}")
if mc_process and not mc_process.stdin.is_closing():
# Add a newline character, as Minecraft expects
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
# Simple request parsing
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
# 1. Start playit.gg
if PLAYIT_SECRET:
playit_command = ['playit', '--secret', PLAYIT_SECRET]
playit_process = await start_subprocess(playit_command)
# Create tasks to forward playit's output to the console logs
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.")
# 2. Start Minecraft server
mc_command = [
'java', f'-Xmx{MINECRAFT_RAM}', '-Xms1G',
'-jar', 'server.jar', 'nogui'
]
mc_process = await start_subprocess(mc_command)
# Create tasks to forward Minecraft's output to websockets
asyncio.create_task(read_stream(mc_process.stdout, ""))
asyncio.create_task(read_stream(mc_process.stderr, "[mc/err] "))
# 3. Start WebSocket and HTTP servers
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}")
# Wait for servers to close (which they won't, unless an error occurs)
await asyncio.gather(
websocket_server.wait_closed(),
http_server.wait_closed()
)
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Server shutting down.") |