why / app.py
yobberbyte's picture
Update app.py
0e0eafc verified
print("STARTED")
# Imports
import gradio as gr
import spaces, requests, os, time, threading, subprocess, shutil, tarfile, urllib.request, json
from huggingface_hub import hf_hub_download, upload_file
# ========================
# PATH SETUP (FIXED)
# ========================
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.join(ROOT_DIR, "minecraft_server")
WORLD_PATH = os.path.join(BASE_DIR, "world")
NETHER_PATH = os.path.join(BASE_DIR, "world_nether")
END_PATH = os.path.join(BASE_DIR, "world_the_end")
print("[DEBUG] ROOT:", ROOT_DIR)
print("[DEBUG] BASE:", BASE_DIR)
# ========================
# ENV VARIABLES
# ========================
ADMIN_PASSWORD = os.environ.get("ADMIN_PASSWORD", "")
HF_TOKEN = os.environ.get("HF_TOKEN", "")
PLAYIT_AUTH_KEY = os.environ.get("PLAYIT_AUTH_KEY", "")
HF_REPO_ID = "Dalleon/McSaves"
WORLD_ID = "world_default"
# ========================
# GLOBALS
# ========================
SERVER_PROCESS = None
TCP_PROCESS = None
PUBLIC_TCP = None
SERVER_ONLINE = False
SERVER_OUTPUT_BUFFER = []
# ========================
# JAVA INSTALL (fallback)
# ========================
def install_java21_local():
jdk_url = "https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz"
download_path = os.path.join(BASE_DIR, "jdk21.tar.gz")
print("[JAVA] Downloading...")
urllib.request.urlretrieve(jdk_url, download_path)
extract_dir = os.path.join(BASE_DIR, "jdk21")
os.makedirs(extract_dir, exist_ok=True)
with tarfile.open(download_path, "r:gz") as tar:
tar.extractall(path=extract_dir)
for item in os.listdir(extract_dir):
if item.startswith("jdk-21"):
java_home = os.path.join(extract_dir, item)
os.environ["JAVA_HOME"] = java_home
os.environ["PATH"] = os.path.join(java_home, "bin") + ":" + os.environ["PATH"]
print("[JAVA] Installed:", java_home)
return
# ========================
# DOWNLOAD PAPER
# ========================
def download_paper():
jar_path = os.path.join(BASE_DIR, "server.jar")
if os.path.exists(jar_path):
return
print("[PAPER] Downloading...")
url = "https://api.papermc.io/v2/projects/paper/versions/1.20.6/builds/latest/downloads/paper-1.20.6-latest.jar"
urllib.request.urlretrieve(url, jar_path)
# ========================
# SERVER OUTPUT READER
# ========================
def read_server_output():
global SERVER_PROCESS
while True:
if SERVER_PROCESS and SERVER_PROCESS.stdout:
line = SERVER_PROCESS.stdout.readline()
if line:
print("[MC]", line.strip())
SERVER_OUTPUT_BUFFER.append(line)
time.sleep(0.1)
# ========================
# SAVE WORLD
# ========================
def save_world():
global SERVER_PROCESS
print("[SAVE] Saving world...")
if SERVER_PROCESS:
SERVER_PROCESS.stdin.write("save-off\n")
SERVER_PROCESS.stdin.flush()
SERVER_PROCESS.stdin.write("save-all\n")
SERVER_PROCESS.stdin.flush()
time.sleep(10)
SERVER_PROCESS.stdin.write("save-on\n")
SERVER_PROCESS.stdin.flush()
tar_path = os.path.join(BASE_DIR, "world.tar.gz")
with tarfile.open(tar_path, "w:gz") as tar:
if os.path.exists(WORLD_PATH):
tar.add(WORLD_PATH, arcname="world")
if os.path.exists(NETHER_PATH):
tar.add(NETHER_PATH, arcname="world_nether")
if os.path.exists(END_PATH):
tar.add(END_PATH, arcname="world_the_end")
upload_file(
path_or_fileobj=tar_path,
path_in_repo="world.tar.gz",
repo_id=HF_REPO_ID,
token=HF_TOKEN,
repo_type="model"
)
print("[SAVE] Done.")
# ========================
# LOAD WORLD
# ========================
def load_world():
print("[LOAD] Loading world...")
try:
file_path = hf_hub_download(
repo_id=HF_REPO_ID,
filename="world.tar.gz",
token=HF_TOKEN
)
except:
print("[LOAD] No backup found.")
return
if os.path.exists(WORLD_PATH):
shutil.rmtree(WORLD_PATH)
with tarfile.open(file_path, "r:gz") as tar:
tar.extractall(BASE_DIR)
print("[LOAD] Done.")
# ========================
# PLAYIT TUNNEL (FIXED - NON-BLOCKING)
# ========================
def start_tcp():
global PUBLIC_TCP, SERVER_ONLINE, TCP_PROCESS
playit_path = os.path.join(BASE_DIR, "playit")
if not os.path.exists(playit_path):
print("[PLAYIT] Downloading...")
url = "https://github.com/playit-cloud/playit-agent/releases/latest/download/playit-linux-amd64"
urllib.request.urlretrieve(url, playit_path)
os.chmod(playit_path, 0o755)
if not PLAYIT_AUTH_KEY:
print("[PLAYIT] Missing auth key")
return
# ✅ FIX: fully detached process (NO PIPE, NO READLINE, NO FREEZE)
TCP_PROCESS = subprocess.Popen(
[playit_path, "--secret", PLAYIT_AUTH_KEY],
cwd=BASE_DIR,
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True
)
print("[PLAYIT] Agent started safely in background")
# small delay so it can initialize without blocking HF
time.sleep(5)
# ❗ IMPORTANT: no blocking log parsing anymore
PUBLIC_TCP = "Check Playit dashboard for address"
SERVER_ONLINE = True
print("[PLAYIT] Ready (no blocking, HF-safe)")
# ========================
# START SERVER
# ========================
def start_server():
global SERVER_PROCESS
load_world()
if shutil.which("java") is None:
install_java21_local()
if shutil.which("java") is None:
print("[ERROR] Java not found")
return
download_paper()
with open(os.path.join(BASE_DIR, "eula.txt"), "w") as f:
f.write("eula=true")
SERVER_PROCESS = subprocess.Popen(
[
shutil.which("java"),
"-Xms512M",
"-Xmx1G",
"-jar",
"server.jar",
"--nogui"
],
cwd=BASE_DIR,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True
)
threading.Thread(target=read_server_output, daemon=True).start()
time.sleep(5)
start_tcp()
# ========================
# COMMAND EXEC
# ========================
def execute_command(password, cmd):
if password != ADMIN_PASSWORD:
return "Wrong password"
if not SERVER_PROCESS:
return "Server not running"
SERVER_PROCESS.stdin.write(cmd + "\n")
SERVER_PROCESS.stdin.flush()
return "Executed"
# ========================
# GET ADDRESS
# ========================
def get_address():
if not SERVER_ONLINE:
return "Server starting..."
return PUBLIC_TCP or "No address yet"
# ========================
# INIT
# ========================
threading.Thread(target=start_server, daemon=True).start()
# ========================
# UI
# ========================
with gr.Blocks() as app:
gr.Markdown("## ⛏️ Minecraft Server")
address = gr.Textbox(label="Server Address")
btn = gr.Button("Get Address")
btn.click(fn=get_address, outputs=address)
app.launch()