From 12039fb8626823c6cd9a1d883b43ca1ae5dcf9df Mon Sep 17 00:00:00 2001 From: Matthew Tran Date: Fri, 11 Apr 2025 03:23:52 -0700 Subject: [PATCH] lower terraria idle draw further --- compose.yml | 2 +- terraria/Dockerfile | 6 +-- terraria/entry.py | 90 +++++++++++++++++++++++++++++++++++++++++++++ terraria/entry.sh | 14 ------- 4 files changed, 93 insertions(+), 19 deletions(-) create mode 100644 terraria/entry.py delete mode 100644 terraria/entry.sh diff --git a/compose.yml b/compose.yml index 99f8b81..5a58a0e 100644 --- a/compose.yml +++ b/compose.yml @@ -112,7 +112,7 @@ services: terraria: restart: always build: terraria/. - entrypoint: ["/bin/sh", "/home/me/entry.sh"] + entrypoint: ["/usr/bin/python3", "/home/me/entry.py"] ports: - "7777:7777" networks: diff --git a/terraria/Dockerfile b/terraria/Dockerfile index 216bd43..e0a2119 100644 --- a/terraria/Dockerfile +++ b/terraria/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu:24.04 RUN apt-get update && apt-get -y upgrade -RUN apt-get install -y wget unzip dotnet-runtime-8.0 +RUN apt-get install -y wget unzip python3 iproute2 dotnet-runtime-8.0 RUN groupadd -g 2002 me && useradd -u 2002 -g 2002 -m me USER me @@ -14,12 +14,10 @@ RUN unzip tModLoader.zip -d server && rm tModLoader.zip RUN chmod +x server/start-tModLoaderServer.sh RUN mkdir server/tModLoader-Logs && touch server/tModLoader-Logs/server.log RUN echo "" > server/LaunchUtils/InstallDotNet.sh -COPY --chown=me:me entry.sh ./ +COPY --chown=me:me entry.py ./ COPY --chown=me:me config.default ./config.txt COPY --chown=me:me password.default ./password.txt COPY --chown=me:me config.tx[t] password.tx[t] ./ # To add mods, install them on the client and copy over the .tmod files to mods/ # Then modify/create mods/enabled.json and add the desired mods to enable - -# TODO reduce idle cpu when no players... diff --git a/terraria/entry.py b/terraria/entry.py new file mode 100644 index 0000000..99c31bd --- /dev/null +++ b/terraria/entry.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 + +import logging +import os +import signal +import socket +import subprocess +import time + +class Runner: + def __init__(self): + self.server = None + signal.signal(signal.SIGINT, self.terminate) + signal.signal(signal.SIGTERM, self.terminate) + + def run(self): + while True: + # low-power idle waiting for players + logging.info("waiting for connection attempt...") + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(("0.0.0.0", 7777)) + s.listen() + (c, addr) = s.accept() + c.close() + s.close() + logging.info(f"attempted connection from {addr}, starting server...") + + # start server + with open("/home/me/password.txt", "r") as f: + password = f.read() + self.server = subprocess.Popen([ + "/bin/bash", + "/home/me/server/start-tModLoaderServer.sh", + "-nosteam", + "-config", "/home/me/config.txt", + "-pass", f"{password}", + ], stdin=subprocess.PIPE, start_new_session=True) + while not self.started(): + time.sleep(1.0) + + # wait until no connections for timeout + logging.info("server started, waiting until idle for 30s...") + idle_start = time.time() + while (time.time() - idle_start) < 30.0: + time.sleep(1.0) + if self.playing(): + idle_start = time.time() + + # stop server + self.exit() + while self.started(): + time.sleep(1.0) + + def started(self): + return b"LISTEN" in subprocess.check_output([ + "ss", "-tln", "( sport = :7777 )"]) + + def playing(self): + return b"ESTAB" in subprocess.check_output([ + "ss", "-tn", "( sport = :7777 )"]) + + def exit(self): + if self.server: + logging.info("shutting down server...") + self.server.stdin.write(b"exit\n") + self.server.stdin.flush() + timeout, start = True, time.time() + while (time.time() - start) < 8.0: + if self.server.poll() is not None: + timeout = False + break + time.sleep(0.25) + if timeout: + logging.error("forcefully shutting down server...") + os.killpg(os.getpgid(self.server.pid), signal.SIGKILL) + self.server = None + + def terminate(self, signum, frame): + self.exit() + exit(0) + +if __name__ == "__main__": + logging.basicConfig( + format="[%(asctime)s] [%(levelname)s] %(message)s", + level=logging.INFO, + datefmt="%Y-%m-%d %H:%M:%S", + ) + runner = Runner() + runner.run() diff --git a/terraria/entry.sh b/terraria/entry.sh deleted file mode 100644 index 7656bbb..0000000 --- a/terraria/entry.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -cleanup() { - echo "exit\n" > cmd -} - -trap 'cleanup' TERM - -rm cmd -mkfifo cmd -tail -f cmd | ./server/start-tModLoaderServer.sh -nosteam -config ../config.txt -pass $(cat password.txt) & -echo "help\n" > cmd # shell waits for FIFO to be opened for writing before starting program! -wait $! # wait for SIGTERM -wait $! # wait for server to stop