Some changes.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
import queue
|
import queue
|
||||||
|
import ssl
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import socket
|
import socket
|
||||||
@@ -31,6 +32,9 @@ class ServerJarClient(Application):
|
|||||||
# Socket
|
# Socket
|
||||||
self.sock = None
|
self.sock = None
|
||||||
|
|
||||||
|
# Args
|
||||||
|
self.args = None
|
||||||
|
|
||||||
# event
|
# event
|
||||||
self.kb = KeyBindings()
|
self.kb = KeyBindings()
|
||||||
|
|
||||||
@@ -281,8 +285,9 @@ class ServerJarClient(Application):
|
|||||||
def arguments_parser(self):
|
def arguments_parser(self):
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
# parser.add_argument("-p", "--port", type=int, help="Port number", required=True)
|
parser.add_argument("-p", "--port", type=int, help="Port number", required=True)
|
||||||
# parser.add_argument('-host', '--host', type=str, help="Hostname", required=True)
|
parser.add_argument('-host', '--host', type=str, help="Hostname", required=True)
|
||||||
|
parser.add_argument('-no-tls', '--no-tls', type="store_true", help="Enable TLS support")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -374,8 +379,17 @@ class ServerJarClient(Application):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self._log(f"Connecting to {self.host}:{self.port} ...")
|
self._log(f"Connecting to {self.host}:{self.port} ...")
|
||||||
|
|
||||||
|
# Create connect
|
||||||
|
if self.args.no_tls:
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
s.connect((self.host, self.port))
|
s.connect((self.host, self.port))
|
||||||
|
else:
|
||||||
|
raw = socket.create_connection((self.host, self.port))
|
||||||
|
context = ssl.create_default_context()
|
||||||
|
s = context.wrap_socket(raw, server_hostname=self.host)
|
||||||
|
|
||||||
|
s.connect((self.host, self.port))
|
||||||
|
|
||||||
with self.sock_lock:
|
with self.sock_lock:
|
||||||
self.sock = s
|
self.sock = s
|
||||||
@@ -421,7 +435,7 @@ class ServerJarClient(Application):
|
|||||||
|
|
||||||
|
|
||||||
def startup(self):
|
def startup(self):
|
||||||
self.arguments_parser()
|
self.args = self.arguments_parser()
|
||||||
self.layout.focus(self.input_area)
|
self.layout.focus(self.input_area)
|
||||||
asyncio.create_task(self.consume_incoming())
|
asyncio.create_task(self.consume_incoming())
|
||||||
|
|
||||||
|
|||||||
@@ -10,30 +10,78 @@ import socketserver
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import queue
|
import queue
|
||||||
|
import ssl
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import click
|
import click
|
||||||
import yaml
|
import yaml
|
||||||
from utils.common import download_latest_paper_jar, get_latest_version_minecraft, get_specific_version_paper_builds, \
|
from utils.common import download_latest_paper_jar, get_latest_version_minecraft, get_specific_version_paper_builds, \
|
||||||
download_server_jar, download_latest_build_paper_jar
|
download_server_jar, download_latest_build_paper_jar, get_latest_paper_version
|
||||||
from utils.file_settings import FileSettings
|
from utils.file_settings import FileSettings
|
||||||
from utils.file_settings import required_list, required_value
|
from utils.file_settings import required_list, required_value
|
||||||
|
from cryptography import x509
|
||||||
|
from cryptography.x509.oid import NameOID
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
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.0"
|
||||||
|
|
||||||
def exit(message):
|
def exit(message):
|
||||||
click.echo(click.style(message, fg='green'))
|
click.echo(click.style(message, fg='green'))
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
def main():
|
def main():
|
||||||
print("ServerJar\n"
|
print(f"ServerJar v{VERSION}"
|
||||||
"WorkDir: {}".format(ROOT_DIR))
|
f"\nWorkDir: {ROOT_DIR}")
|
||||||
|
|
||||||
|
|
||||||
|
def load_settings():
|
||||||
|
s = FileSettings(
|
||||||
|
SERVER_CONFIG_PATH,
|
||||||
|
{
|
||||||
|
"servers": [],
|
||||||
|
"socketServerHostname": "127.0.0.1",
|
||||||
|
"socketServerPort": 25560
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"socketServerHostname": required_value("127.0.0.1"),
|
||||||
|
"socketServerPort": required_value(25560),
|
||||||
|
"enableTLSSupport": required_value(True),
|
||||||
|
"socketServerCertfile": required_value("data/server-public.pem"),
|
||||||
|
"socketServerKeyfile": required_value("data/server-private.pem"),
|
||||||
|
"servers": required_list(
|
||||||
|
{
|
||||||
|
"name": "Unnamed Server",
|
||||||
|
"version": "unknown",
|
||||||
|
"description": "",
|
||||||
|
"args": [],
|
||||||
|
"workDir": "",
|
||||||
|
"port": 25565,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"enable": True
|
||||||
|
},
|
||||||
|
use_same_form=True,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
dumps_func=yaml.safe_dump,
|
||||||
|
load_func=yaml.safe_load,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not s.exists():
|
||||||
|
s.create()
|
||||||
|
|
||||||
|
s.read_from_exist()
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
@main.command()
|
@main.command()
|
||||||
@@ -49,7 +97,27 @@ def main():
|
|||||||
@click.option("--latest", is_flag=True, help="Download latest Minecraft version (With latest build paper)")
|
@click.option("--latest", is_flag=True, help="Download latest Minecraft version (With latest build paper)")
|
||||||
@click.option("--list-builds", is_flag=True, help="List available paper build versions")
|
@click.option("--list-builds", is_flag=True, help="List available paper build versions")
|
||||||
@click.option("--filename", default=None, help="Custom SERVER.jar file name")
|
@click.option("--filename", default=None, help="Custom SERVER.jar file name")
|
||||||
def create_server(name, mc_version, build, snapshot, latest, list_builds, filename):
|
@click.option("--extra-args", "-e",
|
||||||
|
help="Extra java arguments", type=str, default="")
|
||||||
|
@click.option("--custom-args", "-ce",
|
||||||
|
help="Custom arguments (command)", type=str, default="")
|
||||||
|
@click.option("--java-exec-path", "-p", show_default=True,
|
||||||
|
help="The destination of the java executable", default="java")
|
||||||
|
@click.option("--x-memory-initial", "-xms", show_default=True,
|
||||||
|
help="Initial allocation size of the memory for server",
|
||||||
|
type=str, default="1G")
|
||||||
|
@click.option("--x-memory-maximum", "-xmx", show_default=True,
|
||||||
|
help="Maximum allocation size of the memory for server",
|
||||||
|
type=str, default="4G")
|
||||||
|
@click.option("--nogui", "-ng",
|
||||||
|
help="Disable server window",
|
||||||
|
is_flag=True)
|
||||||
|
@click.option("--server-host", "-srh",
|
||||||
|
help="Hostname of the server", required=True)
|
||||||
|
@click.option("--server-port", "-srp",
|
||||||
|
help="Port of the server", required=True)
|
||||||
|
def create_server(name, mc_version, build, snapshot, latest, list_builds, filename, extra_args, java_exec_path,
|
||||||
|
x_memory_initial, x_memory_maximum, nogui, custom_args, server_port, server_host):
|
||||||
server_dir = Path("servers", name)
|
server_dir = Path("servers", name)
|
||||||
|
|
||||||
if server_dir.exists():
|
if server_dir.exists():
|
||||||
@@ -60,14 +128,16 @@ def create_server(name, mc_version, build, snapshot, latest, list_builds, filena
|
|||||||
|
|
||||||
server_dir.mkdir(parents=True, exist_ok=True)
|
server_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
latest_ver = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
release = True if not snapshot else False
|
release = True if not snapshot else False
|
||||||
if latest:
|
if latest:
|
||||||
click.echo("Fetching latest Mojang release version...")
|
click.echo("Fetching latest Mojang release version...")
|
||||||
out = download_latest_paper_jar(server_dir, filename=filename, release=release)
|
latest_ver = get_latest_paper_version(release=release)
|
||||||
click.echo(f"Done: {out}")
|
builds = get_specific_version_paper_builds(latest_ver)
|
||||||
return
|
out = download_server_jar(latest_ver, builds[-1], server_dir)
|
||||||
|
else:
|
||||||
if mc_version is None:
|
if mc_version is None:
|
||||||
click.echo("The mc-version is not specified. Fetching latest Minecraft release version...")
|
click.echo("The mc-version is not specified. Fetching latest Minecraft release version...")
|
||||||
mc_version = get_latest_version_minecraft(release=release)
|
mc_version = get_latest_version_minecraft(release=release)
|
||||||
@@ -94,76 +164,10 @@ def create_server(name, mc_version, build, snapshot, latest, list_builds, filena
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise click.ClickException(str(e))
|
raise click.ClickException(str(e))
|
||||||
|
|
||||||
|
|
||||||
def load_settings():
|
|
||||||
s = FileSettings(
|
|
||||||
SERVER_CONFIG_PATH,
|
|
||||||
{
|
|
||||||
"servers": [],
|
|
||||||
"socketServerHostname": "127.0.0.1",
|
|
||||||
"socketServerPort": 25560
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"socketServerHostname": required_value("127.0.0.1"),
|
|
||||||
"socketServerPort": required_value(25560),
|
|
||||||
"servers": required_list(
|
|
||||||
{
|
|
||||||
"name": "Unnamed Server",
|
|
||||||
"version": "unknown",
|
|
||||||
"description": "",
|
|
||||||
"command": "",
|
|
||||||
"workDir": "",
|
|
||||||
"port": 25565,
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"enable": True
|
|
||||||
},
|
|
||||||
use_same_form=True,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
dumps_func=yaml.safe_dump,
|
|
||||||
load_func=yaml.safe_load,
|
|
||||||
)
|
|
||||||
|
|
||||||
if not s.exists():
|
|
||||||
s.create()
|
|
||||||
|
|
||||||
s.read_from_exist()
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
@main.command()
|
|
||||||
@click.option("--server-folder-path", "-sf",
|
|
||||||
help="The destination of the folder", required=True)
|
|
||||||
@click.option("--server-jar-path", "-sp",
|
|
||||||
help="The destination of the SERVER.jar", required=True)
|
|
||||||
@click.option("--socket-server-host", "-srh",
|
|
||||||
help="Hostname of the socket server", required=True)
|
|
||||||
@click.option("--socket-server-port", "-srp",
|
|
||||||
help="Port of the socket server", required=True)
|
|
||||||
@click.option("--java-exec-path", "-p", show_default=True,
|
|
||||||
help="The destination of the java executable", default="java")
|
|
||||||
@click.option("--x-memory-initial", "-xms", show_default=True,
|
|
||||||
help="Initial allocation size of the memory for server",
|
|
||||||
type=str, default="1G")
|
|
||||||
@click.option("--x-memory-maximum", "-xmx", show_default=True,
|
|
||||||
help="Maximum allocation size of the memory for server",
|
|
||||||
type=str, default="4G")
|
|
||||||
@click.option("--nogui", "-ng",
|
|
||||||
help="Disable server window",
|
|
||||||
is_flag=True)
|
|
||||||
@click.option("--extra-args", "-e",
|
|
||||||
help="Extra java arguments", type=str, default="")
|
|
||||||
@click.option("--custom-commands", "-cd",
|
|
||||||
help="Custom run commands", type=str, default="")
|
|
||||||
def create_bootstrap(server_folder_path, server_jar_path, socket_server_host, socket_server_port,
|
|
||||||
java_exec_path, x_memory_initial, x_memory_maximum, nogui, extra_args, custom_commands):
|
|
||||||
|
|
||||||
settings = load_settings()
|
settings = load_settings()
|
||||||
|
|
||||||
print("There's some information you need to fill for server config.")
|
print("There's some information you need to fill for server config.")
|
||||||
name = str(input("New server name: "))
|
name = str(input("New server name: ")) if name is None else name
|
||||||
version = str(input("Server version: "))
|
|
||||||
desc = str(input("Server description: "))
|
desc = str(input("Server description: "))
|
||||||
|
|
||||||
found_exist = False
|
found_exist = False
|
||||||
@@ -175,33 +179,41 @@ def create_bootstrap(server_folder_path, server_jar_path, socket_server_host, so
|
|||||||
result = str(input("WARNING: Found duplicate server name. Would you like to continue? [y/N] "))
|
result = str(input("WARNING: Found duplicate server name. Would you like to continue? [y/N] "))
|
||||||
if not result.lower() == "y":
|
if not result.lower() == "y":
|
||||||
exit("User aborted.")
|
exit("User aborted.")
|
||||||
|
return
|
||||||
|
|
||||||
extra_args += "nogui" if nogui else ""
|
extra_args += "nogui" if nogui else ""
|
||||||
cmd = f"{java_exec_path} -Xms{x_memory_initial} -Xmx{x_memory_maximum} -jar {server_jar_path} {extra_args}"
|
args = [
|
||||||
|
java_exec_path,
|
||||||
|
"--Xms{}".format(x_memory_initial),
|
||||||
|
"--Xmx{}".format(x_memory_maximum),
|
||||||
|
"-jar",
|
||||||
|
out.absolute().as_posix(),
|
||||||
|
extra_args,
|
||||||
|
]
|
||||||
|
|
||||||
if custom_commands:
|
if custom_args:
|
||||||
print("Will use custom commands as replacement.")
|
print("Will use custom commands as replacement.")
|
||||||
cmd = custom_commands
|
args = custom_args
|
||||||
|
|
||||||
print(f"Server command: {cmd}")
|
print(f"Server command: {" ".join(args)}")
|
||||||
|
|
||||||
with settings.edit() as s:
|
with settings.edit() as s:
|
||||||
print("Saving...")
|
print("Saving...")
|
||||||
s["servers"].append({
|
s["servers"].append({
|
||||||
"name": name,
|
"name": name,
|
||||||
"version": version,
|
"version": latest_ver if latest_ver is not None else mc_version,
|
||||||
"description": desc,
|
"description": desc,
|
||||||
"command": cmd,
|
"args": args,
|
||||||
"workDir": server_folder_path,
|
"workDir": server_dir.absolute().as_posix(),
|
||||||
"port": socket_server_port,
|
"port": server_port,
|
||||||
"host": socket_server_host,
|
"host": server_host,
|
||||||
"enable": True,
|
"enable": True,
|
||||||
})
|
})
|
||||||
|
|
||||||
print("Done")
|
print("Done")
|
||||||
|
|
||||||
class SocketServer:
|
class SocketServer:
|
||||||
def __init__(self, host, port):
|
def __init__(self, host, port, enable_tls, certfile: Path, keyfile: Path):
|
||||||
self.logger = logging.getLogger("SocketServer")
|
self.logger = logging.getLogger("SocketServer")
|
||||||
self.stdout_handler = logging.StreamHandler(sys.stdout)
|
self.stdout_handler = logging.StreamHandler(sys.stdout)
|
||||||
self.stdout_handler.setFormatter(logging.Formatter("%(level)s:%(message)s"))
|
self.stdout_handler.setFormatter(logging.Formatter("%(level)s:%(message)s"))
|
||||||
@@ -220,6 +232,18 @@ class SocketServer:
|
|||||||
self._sub_lock = threading.Lock()
|
self._sub_lock = threading.Lock()
|
||||||
|
|
||||||
self.command_receivers = {}
|
self.command_receivers = {}
|
||||||
|
self.enable_tls = enable_tls
|
||||||
|
self.certfile = certfile
|
||||||
|
self.keyfile = keyfile
|
||||||
|
|
||||||
|
if not self.certfile.exists():
|
||||||
|
raise FileNotFoundError("Certfile not found")
|
||||||
|
|
||||||
|
if not self.keyfile.exists():
|
||||||
|
raise FileNotFoundError("Keyfile not found")
|
||||||
|
|
||||||
|
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||||
|
context.load_cert_chain(certfile=self.certfile, keyfile=self.keyfile)
|
||||||
|
|
||||||
# -------------------------
|
# -------------------------
|
||||||
# Socket Server
|
# Socket Server
|
||||||
@@ -253,6 +277,12 @@ class SocketServer:
|
|||||||
def _build_tcp_server(self):
|
def _build_tcp_server(self):
|
||||||
manager = self
|
manager = self
|
||||||
|
|
||||||
|
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||||
|
try:
|
||||||
|
context.load_cert_chain(certfile=self.certfile, keyfile=self.keyfile)
|
||||||
|
except ssl.SSLError as e:
|
||||||
|
self.logger.fatal("SSL error", exc_info=e)
|
||||||
|
|
||||||
class TCPServer(socketserver.ThreadingTCPServer):
|
class TCPServer(socketserver.ThreadingTCPServer):
|
||||||
allow_reuse_address = True
|
allow_reuse_address = True
|
||||||
daemon_threads = True
|
daemon_threads = True
|
||||||
@@ -261,6 +291,14 @@ class SocketServer:
|
|||||||
super().__init__(server_address, RequestHandlerClass)
|
super().__init__(server_address, RequestHandlerClass)
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
|
|
||||||
|
if self.manager.enable_tls:
|
||||||
|
def get_request():
|
||||||
|
sock, addr = super().get_request()
|
||||||
|
tls_sock = context.wrap_socket(sock, server_side=True)
|
||||||
|
return tls_sock, addr
|
||||||
|
|
||||||
|
self.get_request = get_request
|
||||||
|
|
||||||
class Handler(socketserver.BaseRequestHandler):
|
class Handler(socketserver.BaseRequestHandler):
|
||||||
current_server_record = {
|
current_server_record = {
|
||||||
}
|
}
|
||||||
@@ -442,7 +480,7 @@ class SocketServer:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
class Server:
|
class Server:
|
||||||
def __init__(self, name, version, description, command, work_dir, port, host, enable):
|
def __init__(self, name, version, description, args, work_dir, port, host, enable):
|
||||||
self._stdout_thread = None
|
self._stdout_thread = None
|
||||||
|
|
||||||
# Process
|
# Process
|
||||||
@@ -464,14 +502,14 @@ class Server:
|
|||||||
self.logger = logging.getLogger(name)
|
self.logger = logging.getLogger(name)
|
||||||
self.logger.setLevel(logging.INFO)
|
self.logger.setLevel(logging.INFO)
|
||||||
self.stdout_handler = logging.StreamHandler(sys.stdout)
|
self.stdout_handler = logging.StreamHandler(sys.stdout)
|
||||||
self.stdout_handler.setFormatter(logging.Formatter("[%(asctime)s:%(level)s]: %(message)s"))
|
self.stdout_handler.setFormatter(logging.Formatter(f"[%(asctime)s:%(levelname)s:{name}]: %(message)s"))
|
||||||
self.logger.addHandler(self.stdout_handler)
|
self.logger.addHandler(self.stdout_handler)
|
||||||
|
|
||||||
# Values from config
|
# Values from config
|
||||||
self.name = name
|
self.name = name
|
||||||
self.version = version
|
self.version = version
|
||||||
self.description = description
|
self.description = description
|
||||||
self.command = command
|
self.args = args
|
||||||
self.work_dir = work_dir
|
self.work_dir = work_dir
|
||||||
self.port = port
|
self.port = port
|
||||||
self.host = host
|
self.host = host
|
||||||
@@ -489,14 +527,12 @@ class Server:
|
|||||||
self.logger.warning("[PROC] already running, skip")
|
self.logger.warning("[PROC] already running, skip")
|
||||||
return
|
return
|
||||||
|
|
||||||
args = shlex.split(self.command)
|
if len(self.args) == 0:
|
||||||
if not args:
|
raise Exception("[SYS] No arguments provided")
|
||||||
raise ValueError(f"Server \"{self.name}\" command is empty.")
|
|
||||||
|
|
||||||
self.logger.info("[PROC] spawning: %s", self.command)
|
|
||||||
|
|
||||||
|
self.logger.info("[PROC] spawning: %s", " ".join(self.args))
|
||||||
self.proc = subprocess.Popen(
|
self.proc = subprocess.Popen(
|
||||||
args,
|
self.args,
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
@@ -634,7 +670,7 @@ def load_all_server_from_settings(settings: FileSettings):
|
|||||||
name=server_conf.get("name"),
|
name=server_conf.get("name"),
|
||||||
version=server_conf.get("version"),
|
version=server_conf.get("version"),
|
||||||
description=server_conf.get("description"),
|
description=server_conf.get("description"),
|
||||||
command=server_conf.get("command"),
|
args=server_conf.get("args",[]),
|
||||||
work_dir=server_conf.get("workDir"),
|
work_dir=server_conf.get("workDir"),
|
||||||
port=server_conf.get("port"),
|
port=server_conf.get("port"),
|
||||||
host=server_conf.get("host"),
|
host=server_conf.get("host"),
|
||||||
@@ -647,7 +683,7 @@ def load_all_server_from_settings(settings: FileSettings):
|
|||||||
@main.command()
|
@main.command()
|
||||||
def runserver():
|
def runserver():
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
formatter = logging.Formatter('%(asctime)s:%(levelname)s: %(message)s')
|
formatter = logging.Formatter('[%(asctime)s:%(levelname)s:runServer]: %(message)s')
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
stdout_handler = logging.StreamHandler(sys.stdout)
|
stdout_handler = logging.StreamHandler(sys.stdout)
|
||||||
stdout_handler.setFormatter(formatter)
|
stdout_handler.setFormatter(formatter)
|
||||||
@@ -659,10 +695,11 @@ def runserver():
|
|||||||
logger.info("{} servers available".format(len(servers)))
|
logger.info("{} servers available".format(len(servers)))
|
||||||
|
|
||||||
# Socket
|
# Socket
|
||||||
logger.info("Starting socket server")
|
|
||||||
socket_server = SocketServer(settings.get("socketServerHostname", "127.0.0.1"),
|
socket_server = SocketServer(settings.get("socketServerHostname", "127.0.0.1"),
|
||||||
settings.get("socketServerPort", 25560))
|
settings.get("socketServerPort", 25560),
|
||||||
socket_server.start_socket_server()
|
settings.get("enableTLSSupport", True),
|
||||||
|
Path(settings.get("socketServerCertfile", "data/server.crt")),
|
||||||
|
Path(settings.get( "socketServerKeyfile", "data/server.key")))
|
||||||
|
|
||||||
# Flags
|
# Flags
|
||||||
stop_once = False
|
stop_once = False
|
||||||
@@ -693,6 +730,10 @@ def runserver():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Boot server
|
# Boot server
|
||||||
|
if len(servers) != 0:
|
||||||
|
logger.info("Starting socket server")
|
||||||
|
socket_server.start_socket_server()
|
||||||
|
|
||||||
logger.info("Starting server")
|
logger.info("Starting server")
|
||||||
for server in servers:
|
for server in servers:
|
||||||
if server.enable:
|
if server.enable:
|
||||||
@@ -731,9 +772,66 @@ def runserver():
|
|||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.info("Stopping server...")
|
logger.info("Stopping server...")
|
||||||
cleanup()
|
cleanup()
|
||||||
|
else:
|
||||||
|
logger.info("No work to do.")
|
||||||
|
|
||||||
|
settings.save()
|
||||||
|
|
||||||
logger.info("Stopped!")
|
logger.info("Stopped!")
|
||||||
|
|
||||||
|
@main.command()
|
||||||
|
def generate_tls_key():
|
||||||
|
print("Generating TLS key...")
|
||||||
|
key = rsa.generate_private_key(
|
||||||
|
public_exponent=65537,
|
||||||
|
key_size=2048,
|
||||||
|
)
|
||||||
|
|
||||||
|
subject = issuer = x509.Name([
|
||||||
|
x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
|
||||||
|
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"California"),
|
||||||
|
x509.NameAttribute(NameOID.LOCALITY_NAME, u"San Francisco"),
|
||||||
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My Dev Org"),
|
||||||
|
x509.NameAttribute(NameOID.COMMON_NAME, u"localhost"),
|
||||||
|
])
|
||||||
|
|
||||||
|
cert = x509.CertificateBuilder().subject_name(
|
||||||
|
subject
|
||||||
|
).issuer_name(
|
||||||
|
issuer
|
||||||
|
).public_key(
|
||||||
|
key.public_key()
|
||||||
|
).serial_number(
|
||||||
|
x509.random_serial_number()
|
||||||
|
).not_valid_before(
|
||||||
|
datetime.datetime.now(datetime.UTC)
|
||||||
|
).not_valid_after(
|
||||||
|
# Valid for 1 year
|
||||||
|
datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=365)
|
||||||
|
).add_extension(
|
||||||
|
x509.SubjectAlternativeName([x509.DNSName(u"localhost")]),
|
||||||
|
critical=False,
|
||||||
|
).sign(key, hashes.SHA256())
|
||||||
|
|
||||||
|
private_key = Path("data/server-private.pem")
|
||||||
|
public_key = Path("data/server-public.pem")
|
||||||
|
private_key.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
public_key.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
with private_key.open("wb") as f:
|
||||||
|
f.write(key.private_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||||
|
encryption_algorithm=serialization.NoEncryption(),
|
||||||
|
))
|
||||||
|
|
||||||
|
with public_key.open("wb") as f:
|
||||||
|
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
||||||
|
|
||||||
|
print("Private key saved to {}".format(private_key))
|
||||||
|
print("Public key saved to {}".format(public_key))
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
+29
-3
@@ -9,7 +9,7 @@ MOJANG_VERSION_MANIFEST_V2 = "https://piston-meta.mojang.com/mc/game/version_man
|
|||||||
|
|
||||||
|
|
||||||
def download_file(url: str, destination: Path, chunk_size: int = 1024 * 512):
|
def download_file(url: str, destination: Path, chunk_size: int = 1024 * 512):
|
||||||
destination.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:
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
@@ -17,7 +17,7 @@ def download_file(url: str, destination: Path, chunk_size: int = 1024 * 512):
|
|||||||
|
|
||||||
total = int(r.headers.get("content-length", 0))
|
total = int(r.headers.get("content-length", 0))
|
||||||
|
|
||||||
with destination.open(mode="wb", buffering=chunk_size).write(r.content) as f:
|
with destination.open(mode="wb", buffering=chunk_size) as f:
|
||||||
if total > 0:
|
if total > 0:
|
||||||
with click.progressbar(length=total, label=f"Downloading {os.path.basename(destination)}") as bar:
|
with click.progressbar(length=total, label=f"Downloading {os.path.basename(destination)}") as bar:
|
||||||
for chunk in r.iter_content(chunk_size=chunk_size):
|
for chunk in r.iter_content(chunk_size=chunk_size):
|
||||||
@@ -72,7 +72,7 @@ def get_version_list(release=True):
|
|||||||
|
|
||||||
def get_latest_version_minecraft(release=True):
|
def get_latest_version_minecraft(release=True):
|
||||||
version_list = get_version_list(release=release)
|
version_list = get_version_list(release=release)
|
||||||
ver = version_list[0].get("id") if version_list else None
|
ver = version_list[0] if version_list else None
|
||||||
|
|
||||||
if ver is None:
|
if ver is None:
|
||||||
raise Exception("Unable to find latest version in version list.\n")
|
raise Exception("Unable to find latest version in version list.\n")
|
||||||
@@ -115,6 +115,31 @@ def download_latest_build_paper_jar(minecraft_version: str, destination_dir: Pat
|
|||||||
build = get_latest_build_of_version(minecraft_version)
|
build = get_latest_build_of_version(minecraft_version)
|
||||||
return download_server_jar(minecraft_version, build, destination_dir, filename=filename)
|
return download_server_jar(minecraft_version, build, destination_dir, filename=filename)
|
||||||
|
|
||||||
|
def version_exist_from_paper(minecraft_version: str) -> bool:
|
||||||
|
try:
|
||||||
|
get_specific_version_paper_builds(minecraft_version)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_latest_paper_version(release) -> str:
|
||||||
|
vers = get_version_list(release=release)
|
||||||
|
index = 0
|
||||||
|
latest_paper_support_ver = None
|
||||||
|
|
||||||
|
while latest_paper_support_ver is None:
|
||||||
|
if len(vers) < index+1:
|
||||||
|
raise Exception("No supported Minecraft version available for Paper support.")
|
||||||
|
|
||||||
|
if version_exist_from_paper(minecraft_version=vers[index]):
|
||||||
|
latest_paper_support_ver = vers[index]
|
||||||
|
break
|
||||||
|
|
||||||
|
index+=1
|
||||||
|
|
||||||
|
return latest_paper_support_ver
|
||||||
|
|
||||||
|
|
||||||
def download_latest_paper_jar(destination_dir: Path, filename: str | None = None, release: bool = True):
|
def download_latest_paper_jar(destination_dir: Path, filename: str | None = None, release: bool = True):
|
||||||
"""
|
"""
|
||||||
@@ -126,4 +151,5 @@ def download_latest_paper_jar(destination_dir: Path, filename: str | None = None
|
|||||||
raise Exception("No versions available for Minecraft (Did the server return wrong response ?)")
|
raise Exception("No versions available for Minecraft (Did the server return wrong response ?)")
|
||||||
|
|
||||||
latest_mc = vers[0]
|
latest_mc = vers[0]
|
||||||
|
|
||||||
return download_latest_build_paper_jar(latest_mc, destination_dir, filename=filename)
|
return download_latest_build_paper_jar(latest_mc, destination_dir, filename=filename)
|
||||||
@@ -270,9 +270,15 @@ class FileSettings:
|
|||||||
def _validate_dict_form(self, sample, target):
|
def _validate_dict_form(self, sample, target):
|
||||||
for k, v in sample.items():
|
for k, v in sample.items():
|
||||||
if target.get(k, None) is None:
|
if target.get(k, None) is None:
|
||||||
target[k] = v
|
target[k] = copy.deepcopy(v)
|
||||||
elif isinstance(v, dict) and isinstance(target.get(k, None), dict):
|
elif isinstance(v, dict) and isinstance(target.get(k, None), dict):
|
||||||
target[k] = self._validate_dict_form(v, target.get(k, {}))
|
target[k] = self._validate_dict_form(v, target.get(k, {}))
|
||||||
|
elif type(target.get(k, None)) is not type(v):
|
||||||
|
candidate = target[k].get("default") if isinstance(target[k], dict) else None
|
||||||
|
if type(candidate) is type(v):
|
||||||
|
target[k] = copy.deepcopy(candidate)
|
||||||
|
else:
|
||||||
|
target[k] = copy.deepcopy(v)
|
||||||
|
|
||||||
return target
|
return target
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user