81 lines
4.2 KiB
Python
81 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Build a Linux server bootstrap kit; never install into an existing server."""
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
from urllib.request import urlopen
|
|
import zipfile
|
|
|
|
import pack
|
|
|
|
TOOLS = {
|
|
'fabric-server-launch.jar': (
|
|
'https://meta.fabricmc.net/v2/versions/loader/26.3/0.19.5/1.1.2/server/jar',
|
|
'0b56ad54d762172e8b8748e467f584f071e4dedecd93dc336cf2c68837e790be'),
|
|
'packwiz-installer.jar': (
|
|
'https://github.com/packwiz/packwiz-installer/releases/download/v0.5.14/packwiz-installer.jar',
|
|
'c9f646908d340d84773948a9a7d98bc1dae250d35e1016dc6e2b8459760b5598'),
|
|
'packwiz-installer-bootstrap.jar': (pack.BOOTSTRAP_URL, pack.BOOTSTRAP_SHA256),
|
|
}
|
|
PACK_URL = 'https://git.botsu.net/koka/sanctuary-beta/raw/branch/packwiz/server/pack.toml'
|
|
|
|
|
|
def put(archive, name, data, mode=0o100644):
|
|
entry = zipfile.ZipInfo(name, date_time=(1980, 1, 1, 0, 0, 0))
|
|
entry.compress_type = zipfile.ZIP_DEFLATED
|
|
entry.external_attr = mode << 16
|
|
archive.writestr(entry, data)
|
|
|
|
|
|
def assemble():
|
|
values = pack.properties()
|
|
manifest, _ = pack.verify_server(pack.ROOT / 'build/packwiz-server')
|
|
pack.require(manifest['versions'] == {'minecraft': '26.3', 'fabric': '0.19.5'},
|
|
'Re-pin the server launcher for an explicit Minecraft/Fabric migration')
|
|
cache = pack.ROOT / 'build/server-tools'
|
|
cache.mkdir(parents=True, exist_ok=True)
|
|
for name, (url, digest) in TOOLS.items():
|
|
target = cache / name
|
|
if not target.is_file():
|
|
with urlopen(url, timeout=45) as response:
|
|
data = response.read()
|
|
pack.require(hashlib.sha256(data).hexdigest() == digest, 'Server tool hash mismatch: ' + name)
|
|
target.write_bytes(data)
|
|
pack.require(pack.sha256(target) == digest, 'Cached server tool hash mismatch: ' + name)
|
|
platform = {'versions': manifest['versions'], 'pack_url': PACK_URL,
|
|
'sha256': {name: digest for name, (_, digest) in TOOLS.items()}}
|
|
output = pack.ROOT / 'build' / f'Sanctuary-Server-{values["pack_version"]}.zip'
|
|
with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as archive:
|
|
for name in TOOLS:
|
|
put(archive, '.tools/' + name, (cache / name).read_bytes())
|
|
put(archive, '.tools/server.py', (pack.ROOT / 'scripts/server_runtime.py').read_bytes())
|
|
put(archive, '.tools/platform.json', json.dumps(platform, indent=2) + '\n')
|
|
put(archive, 'server.sh', '#!/bin/sh\nset -eu\ncd "$(dirname "$0")"\nexec python3 .tools/server.py "$@"\n', mode=0o100755)
|
|
put(archive, 'README.md', (pack.ROOT / 'docs/server-pack.md').read_bytes())
|
|
# Examples are deliberately outside config/ and outside the pack index.
|
|
put(archive, 'examples/sanctuary-community.json.example', json.dumps({
|
|
'mode': 'database', 'serverId': 'REPLACE_WITH_EXISTING_SERVER_UUID',
|
|
'jdbcUrl': 'jdbc:mariadb://db.example.net:3306/sanctuary?sslMode=verify-full',
|
|
'user': 'sanctuary_game', 'passwordEnv': 'SANCTUARY_COMMUNITY_DB_PASSWORD',
|
|
}, indent=2) + '\n')
|
|
put(archive, 'examples/sanctuary.env.example',
|
|
'SANCTUARY_APP_URL=https://sanctuary-minecraft.net\n'
|
|
'SANCTUARY_API_TOKEN=\nSANCTUARY_COMMUNITY_DB_PASSWORD=\n'
|
|
'SANCTUARY_MEMORY=4G\n')
|
|
put(archive, 'examples/sanctuary-beta.service',
|
|
'[Unit]\nDescription=Sanctuary Beta Minecraft server\n'
|
|
'Wants=network-online.target\nAfter=network-online.target\n\n'
|
|
'[Service]\nType=simple\nUser=minecraft\nGroup=minecraft\n'
|
|
'WorkingDirectory=/srv/minecraft/sanctuary-beta\n'
|
|
'EnvironmentFile=/etc/sanctuary/beta.env\n'
|
|
'ExecStart=/srv/minecraft/sanctuary-beta/server.sh start\n'
|
|
'Restart=on-failure\nRestartSec=30\nTimeoutStopSec=90\nKillMode=mixed\n'
|
|
'UMask=0077\nNoNewPrivileges=true\nPrivateTmp=true\nProtectHome=true\n'
|
|
'ProtectSystem=strict\nReadWritePaths=/srv/minecraft/sanctuary-beta\n'
|
|
'LimitNOFILE=65535\n\n[Install]\nWantedBy=multi-user.target\n')
|
|
print('Server bootstrap kit: ' + str(output))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
assemble()
|