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.")