Compare commits
11 Commits
79a97cfd06
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5acc033cad | |||
| 7f5c4e11b7 | |||
| 09256ee9f3 | |||
| fed9e3de71 | |||
| 196322b49f | |||
| 0f4af9e1f0 | |||
| d422f5e0df | |||
| dd079bdbc7 | |||
| b7ff3cba4c | |||
| 7d4846225f | |||
| 91ce4c32ff |
@@ -1,8 +1,13 @@
|
|||||||
|
if [ ! -d "$DIR" ]; then
|
||||||
|
uv venv
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Checking environment..."
|
echo "Checking environment..."
|
||||||
if command -v nuitka &> /dev/null; then
|
if command -v nuitka &> /dev/null; then
|
||||||
echo "Nuitka installed"
|
echo "Nuitka installed"
|
||||||
else
|
else
|
||||||
echo "Nuitka not installed. Install it via \"uv pip install nuitka\" !" >&2
|
echo "Nuitka not installed. Install it via \"uv pip install nuitka\" !" >&2
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
nuitka --onefile --standalone --output-filename=serverjar main.py --output-filename=serverjar
|
nuitka --onefile --standalone --output-filename=serverjar main.py --output-filename=serverjar
|
||||||
|
|||||||
@@ -762,6 +762,10 @@ class ServerJarClient(Application):
|
|||||||
context = self.get_tls_context()
|
context = self.get_tls_context()
|
||||||
s = context.wrap_socket(raw, server_hostname=self.host)
|
s = context.wrap_socket(raw, server_hostname=self.host)
|
||||||
|
|
||||||
|
# The connection timeout should not become an idle read timeout.
|
||||||
|
# Keep recv() blocking so quiet but healthy connections stay open.
|
||||||
|
s.settimeout(None)
|
||||||
|
|
||||||
with self.sock_lock:
|
with self.sock_lock:
|
||||||
self.sock = s
|
self.sock = s
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
echo "Checking environment..."
|
||||||
|
if command -v uv &> /dev/null; then
|
||||||
|
echo "uv installed"
|
||||||
|
else
|
||||||
|
echo "uv not installed. Install it via \"dnf install uv\" or \"apt install uv\" !" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ ! -d "$DIR" ]; then
|
||||||
|
uv venv
|
||||||
|
fi
|
||||||
|
uv pip install -r requirements.txt
|
||||||
Regular → Executable
+1
-1
@@ -7,6 +7,6 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|||||||
sudo install -Dm755 "$SCRIPT_DIR/sarclient" /usr/local/bin/sarclient
|
sudo install -Dm755 "$SCRIPT_DIR/sarclient" /usr/local/bin/sarclient
|
||||||
sudo install -Dm755 "$SCRIPT_DIR/serverjar" /usr/local/bin/serverjar
|
sudo install -Dm755 "$SCRIPT_DIR/serverjar" /usr/local/bin/serverjar
|
||||||
|
|
||||||
echo "安裝完成"
|
echo "Install completed"
|
||||||
echo "sarclient -> /usr/local/bin/sarclient"
|
echo "sarclient -> /usr/local/bin/sarclient"
|
||||||
echo "serverjar -> /usr/local/bin/serverjar"
|
echo "serverjar -> /usr/local/bin/serverjar"
|
||||||
@@ -41,7 +41,7 @@ from cryptography.hazmat.primitives import serialization
|
|||||||
|
|
||||||
ROOT_DIR = Path(os.getcwd())
|
ROOT_DIR = Path(os.getcwd())
|
||||||
SERVER_CONFIG_PATH = ROOT_DIR / "config" / "server.yml"
|
SERVER_CONFIG_PATH = ROOT_DIR / "config" / "server.yml"
|
||||||
VERSION = "1.1"
|
VERSION = "1.2"
|
||||||
LOG_DIR_NAME = "logs"
|
LOG_DIR_NAME = "logs"
|
||||||
SERVERJAR_LOG_FILE = "serverjar.log"
|
SERVERJAR_LOG_FILE = "serverjar.log"
|
||||||
LOG_DOWNLOAD_CHUNK_SIZE = 4096
|
LOG_DOWNLOAD_CHUNK_SIZE = 4096
|
||||||
@@ -82,7 +82,7 @@ def load_settings():
|
|||||||
"workDir": "",
|
"workDir": "",
|
||||||
"port": 25565,
|
"port": 25565,
|
||||||
"host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
"enable": True
|
"enable": True,
|
||||||
},
|
},
|
||||||
use_same_form=True,
|
use_same_form=True,
|
||||||
)
|
)
|
||||||
@@ -417,9 +417,10 @@ def resolve_java_executable(java_exec_path, minecraft_version, release, auto_ins
|
|||||||
help="Port of the server", required=True)
|
help="Port of the server", required=True)
|
||||||
@click.option("--server-property", "-sp", "server_properties", multiple=True,
|
@click.option("--server-property", "-sp", "server_properties", multiple=True,
|
||||||
help="server.properties override written after the first startup. Use key=value; can be repeated.")
|
help="server.properties override written after the first startup. Use key=value; can be repeated.")
|
||||||
|
@click.option("--order", "-o", help="Startup order when starting the server", type=int, default=None)
|
||||||
def create_server(name, mc_version, build, server_type, snapshot, latest, list_builds, filename, extra_args, java_exec_path,
|
def create_server(name, mc_version, build, server_type, snapshot, latest, list_builds, filename, extra_args, java_exec_path,
|
||||||
install_java, x_memory_initial, x_memory_maximum, nogui, custom_args, server_port, server_host,
|
install_java, x_memory_initial, x_memory_maximum, nogui, custom_args, server_port, server_host,
|
||||||
server_properties):
|
server_properties, order):
|
||||||
server_dir = Path("servers", name)
|
server_dir = Path("servers", name)
|
||||||
server_type = server_type.lower()
|
server_type = server_type.lower()
|
||||||
property_overrides = parse_server_property_overrides(server_properties)
|
property_overrides = parse_server_property_overrides(server_properties)
|
||||||
@@ -550,6 +551,7 @@ def create_server(name, mc_version, build, server_type, snapshot, latest, list_b
|
|||||||
"port": server_port,
|
"port": server_port,
|
||||||
"host": server_host,
|
"host": server_host,
|
||||||
"enable": True,
|
"enable": True,
|
||||||
|
"order": order
|
||||||
})
|
})
|
||||||
|
|
||||||
print("Done")
|
print("Done")
|
||||||
@@ -1006,6 +1008,16 @@ class SocketServer:
|
|||||||
except (ConnectionResetError, OSError):
|
except (ConnectionResetError, OSError):
|
||||||
mgr.logger.info(
|
mgr.logger.info(
|
||||||
"[SYS] Client disconnected. From {}:{}".format(self.client_address[0], self.client_address[1]))
|
"[SYS] Client disconnected. From {}:{}".format(self.client_address[0], self.client_address[1]))
|
||||||
|
except Exception as e:
|
||||||
|
mgr.logger.exception(
|
||||||
|
"[SYS] Client handler error. From %s:%s",
|
||||||
|
self.client_address[0],
|
||||||
|
self.client_address[1],
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
self.request.sendall(f"[SYS:ERR] Server handler error: {e}\n".encode("utf-8"))
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
finally:
|
finally:
|
||||||
stop_evt.set()
|
stop_evt.set()
|
||||||
if log_q is not None:
|
if log_q is not None:
|
||||||
@@ -1335,6 +1347,9 @@ class Server:
|
|||||||
f"port: {self.port}\n"
|
f"port: {self.port}\n"
|
||||||
f"version: {self.version}\n"
|
f"version: {self.version}\n"
|
||||||
f"status: {"Running" if self.is_process_alive() else "Died"}")
|
f"status: {"Running" if self.is_process_alive() else "Died"}")
|
||||||
|
elif command == "__restart":
|
||||||
|
self.restart()
|
||||||
|
return True, f"Server \"{self.name}\" process restarted"
|
||||||
else:
|
else:
|
||||||
return False, f"Unknown command: {command}"
|
return False, f"Unknown command: {command}"
|
||||||
|
|
||||||
@@ -1356,16 +1371,22 @@ def load_all_server_from_settings(settings: FileSettings):
|
|||||||
servers = []
|
servers = []
|
||||||
with settings.edit() as s:
|
with settings.edit() as s:
|
||||||
for server_conf in s.get("servers", []):
|
for server_conf in s.get("servers", []):
|
||||||
servers.append(Server(
|
order = server_conf.get("order", None)
|
||||||
name=server_conf.get("name"),
|
server = Server(
|
||||||
version=server_conf.get("version"),
|
name=server_conf.get("name"),
|
||||||
description=server_conf.get("description"),
|
version=server_conf.get("version"),
|
||||||
args=server_conf.get("args",[]),
|
description=server_conf.get("description"),
|
||||||
work_dir=server_conf.get("workDir"),
|
args=server_conf.get("args", []),
|
||||||
port=server_conf.get("port"),
|
work_dir=server_conf.get("workDir"),
|
||||||
host=server_conf.get("host"),
|
port=server_conf.get("port"),
|
||||||
enable=server_conf.get("enable")
|
host=server_conf.get("host"),
|
||||||
))
|
enable=server_conf.get("enable")
|
||||||
|
)
|
||||||
|
|
||||||
|
if order is None or order-1<0:
|
||||||
|
servers.append(server)
|
||||||
|
else:
|
||||||
|
servers.insert(order-1, server)
|
||||||
|
|
||||||
return servers
|
return servers
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
prompt-toolkit==3.0.52
|
prompt-toolkit==3.0.52
|
||||||
cryptography==48.0.0
|
cryptography==48.0.0
|
||||||
|
jproperties==2.1.2
|
||||||
|
nuitka==4.1.2
|
||||||
|
click==8.4.1
|
||||||
|
pyyaml==6.0.3
|
||||||
|
requests==2.34.2
|
||||||
jproperties==2.1.2
|
jproperties==2.1.2
|
||||||
+39
-7
@@ -1,11 +1,13 @@
|
|||||||
|
import hashlib
|
||||||
|
import warnings
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import requests
|
import requests
|
||||||
import click
|
import click
|
||||||
import os
|
import os
|
||||||
from jproperties import Properties
|
from jproperties import Properties
|
||||||
|
|
||||||
PAPER_VERSION_API = "https://api.papermc.io/v2/projects/paper/versions/{}"
|
PAPER_VERSION_API = "https://fill.papermc.io/v3/projects/paper/versions/{}"
|
||||||
PAPER_SERVER_JAR_API = "https://api.papermc.io/v2/projects/paper/versions/{}/builds/{}/downloads/paper-{}-{}.jar"
|
PAPER_BUILD_API = "https://fill.papermc.io/v3/projects/paper/versions/{}/builds/{}"
|
||||||
MOJANG_VERSION_MANIFEST_V2 = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"
|
MOJANG_VERSION_MANIFEST_V2 = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"
|
||||||
|
|
||||||
|
|
||||||
@@ -69,7 +71,7 @@ def jar_filename(filename: str | None, default: str) -> str:
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def download_file(url: str, destination: Path, chunk_size: int = 1024 * 512):
|
def download_file(url: str, destination: Path, chunk_size: int = 1024 * 512, sha256=None):
|
||||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
with requests.get(url, stream=True, timeout=30) as r:
|
with requests.get(url, stream=True, timeout=30) as r:
|
||||||
@@ -91,6 +93,19 @@ def download_file(url: str, destination: Path, chunk_size: int = 1024 * 512):
|
|||||||
if chunk:
|
if chunk:
|
||||||
f.write(chunk)
|
f.write(chunk)
|
||||||
|
|
||||||
|
if sha256:
|
||||||
|
sha256_hash = hashlib.sha256()
|
||||||
|
|
||||||
|
with destination.open(mode="rb", buffering=chunk_size) as f:
|
||||||
|
for chunk in iter(lambda: f.read(chunk_size), b""):
|
||||||
|
sha256_hash.update(chunk)
|
||||||
|
|
||||||
|
hexdigest = sha256_hash.hexdigest()
|
||||||
|
|
||||||
|
if hexdigest == sha256:
|
||||||
|
click.echo(f"File {os.path.basename(destination)} verified.")
|
||||||
|
else:
|
||||||
|
raise RuntimeError(f"Download {os.path.basename(destination)} failed. Hash does not match.")
|
||||||
|
|
||||||
def get_specific_version_paper_builds(minecraft_version: str) -> list[dict[str, str]]:
|
def get_specific_version_paper_builds(minecraft_version: str) -> list[dict[str, str]]:
|
||||||
"""
|
"""
|
||||||
@@ -203,24 +218,41 @@ def get_specific_version_minecraft_require_java_version(minecraft_version, relea
|
|||||||
|
|
||||||
return metadata.get("javaVersion", {}).get("majorVersion", None)
|
return metadata.get("javaVersion", {}).get("majorVersion", None)
|
||||||
|
|
||||||
|
def get_paper_server_jar_info(minecraft_version: str, build_version: str):
|
||||||
|
url = PAPER_BUILD_API.format(minecraft_version, build_version)
|
||||||
|
|
||||||
|
try:
|
||||||
|
r = requests.get(url)
|
||||||
|
if r.status_code == 200:
|
||||||
|
return r.json().get("downloads", {}).get("server:default", None)
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unable to fetch paper server jar information. (VER:{minecraft_version},BUILD:{build_version})\n")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
raise Exception(f"Unable to get paper version information. Exec: {e}\n")
|
||||||
|
|
||||||
def download_server_jar(minecraft_version: str, build_version: str, destination: Path, filename: str | None = None):
|
def download_server_jar(minecraft_version: str, build_version: str, destination: Path, filename: str | None = None):
|
||||||
"""
|
"""
|
||||||
Download server jar (paper server only)
|
Download server jar (paper server only)
|
||||||
"""
|
"""
|
||||||
url = PAPER_SERVER_JAR_API.format(minecraft_version, build_version, minecraft_version, build_version)
|
data = get_paper_server_jar_info(minecraft_version, build_version)
|
||||||
|
|
||||||
jar_name = jar_filename(filename, os.path.basename(url))
|
download_url = data.get("url", None)
|
||||||
|
sha256 = data.get("checksums", {}).get("sha256", None)
|
||||||
|
|
||||||
|
jar_name = jar_filename(filename, os.path.basename(download_url))
|
||||||
|
|
||||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||||
destination = Path(destination, jar_name)
|
destination = Path(destination, jar_name)
|
||||||
|
|
||||||
|
if not sha256:
|
||||||
|
warnings.warn("Unable to verify server jar due to hash from server is invalid.", UserWarning)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
download_file(url, destination)
|
download_file(download_url, destination, sha256=sha256)
|
||||||
return destination
|
return destination
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Unable to download server jar for version {}\nURL: {}\nError: {}".format(minecraft_version, url, e))
|
"Unable to download server jar for version {}\nURL: {}\nError: {}".format(minecraft_version, download_url, e))
|
||||||
|
|
||||||
|
|
||||||
def get_latest_build_of_version(minecraft_version: str) -> str:
|
def get_latest_build_of_version(minecraft_version: str) -> str:
|
||||||
|
|||||||
Reference in New Issue
Block a user