quorate/services/minecraft/bootstrap.fsx

131 lines
5.2 KiB
Text
Raw Permalink Normal View History

module Bootstrap
open System
open System.IO
open System.Runtime.InteropServices
open System.Security.Cryptography
open System.Text
let digest path =
use stream = File.OpenRead path
SHA256.HashData stream |> Convert.ToHexString |> _.ToLowerInvariant()
let write (path: string) (bytes: byte[]) =
Directory.CreateDirectory(Path.GetDirectoryName path) |> ignore
File.WriteAllBytes(path, bytes)
let text path value = write path (Encoding.UTF8.GetBytes(value: string))
let trust source runtime =
write (Path.Combine(runtime, "cacerts")) (File.ReadAllBytes source)
type Paths =
{ Data: string
State: string
Backups: string
Files: string }
type Config =
{ Paths: Paths
PasswordPath: string
Eula: string
Args: string
Saltatory: string }
let prefixes =
[ "mods", "mods"
"config", "config"
"defaultconfigs", "defaultconfigs"
"kubejs", "kubejs"
"libraries", "libraries"
"libraries-integratedscripting", "libraries-integratedscripting"
"settings", "settings"
"tools", "tools" ]
let parse (value: string) =
value.Split('\n', StringSplitOptions.RemoveEmptyEntries)
|> Array.map (fun line ->
let line = line.TrimEnd '\r'
let hash, path = line.Substring(0, 64), line.Substring(66)
hash, path)
let sync cache prefix inventory includeFile fetchManifest fetch =
let root = Path.Combine(cache, prefix)
Directory.CreateDirectory root |> ignore
let files = fetchManifest inventory |> parse |> Array.filter (snd >> includeFile)
for hash, name in files do
let path = Path.Combine(root, name)
Directory.CreateDirectory(Path.GetDirectoryName path) |> ignore
fetch (prefix + "/" + name) hash path
let names = files |> Array.map (snd >> fun name -> Path.GetFullPath(Path.Combine(root, name))) |> Set.ofArray
for path in Directory.EnumerateFiles(root, "*", SearchOption.AllDirectories) do
if not (names.Contains(Path.GetFullPath path)) then
File.Delete path
files
let copyFiles source target files =
for _, name in files do
let path = Path.Combine(target, name)
Directory.CreateDirectory(Path.GetDirectoryName path) |> ignore
File.Copy(Path.Combine(source, name), path, true)
let run config arch fetchManifest fetch fetchSaltatory =
let p = config.Paths
let cache = Path.Combine(p.State, "cache")
Directory.CreateDirectory p.Data |> ignore
Directory.CreateDirectory p.Backups |> ignore
Directory.CreateDirectory(Path.Combine(p.State, "world")) |> ignore
Directory.CreateDirectory(Path.Combine(p.State, "world/data/chunky")) |> ignore
for prefix, target in prefixes do
let includeFile (name: string) =
(prefix <> "mods" || not (name.StartsWith("saltatory-", StringComparison.OrdinalIgnoreCase)))
&& (prefix <> "config" || not (name.StartsWith("chunky/tasks/", StringComparison.Ordinal)))
let files = sync cache prefix (prefix + ".sha256") includeFile fetchManifest fetch
let destination = Path.Combine(p.Data, target)
if Directory.Exists destination then Directory.Delete(destination, true)
copyFiles (Path.Combine(cache, prefix)) destination files
let saltatory = Path.Combine(cache, "saltatory.jar")
fetchSaltatory config.Saltatory saltatory
File.Copy(saltatory, Path.Combine(p.Data, "mods", "saltatory.jar"), true)
for name in [ "ops.json"; "whitelist.json"; "banned-ips.json"; "banned-players.json"; "usercache.json" ] do
let path = Path.Combine(p.State, name)
if not (File.Exists path) then text path "[]"
let names = Path.Combine(p.State, "usernamecache.json")
if not (File.Exists names) then text names "{}"
for name in [ "local"; "journeymap" ] do
Directory.CreateDirectory(Path.Combine(p.State, name)) |> ignore
let world = Path.Combine(p.State, "world")
for prefix in [ "datapacks"; "serverconfig" ] do
let name = "world/" + prefix
let files = sync cache name ("world-" + prefix + ".sha256") (fun _ -> true) fetchManifest fetch
copyFiles (Path.Combine(cache, name)) (Path.Combine(world, prefix)) files
let tools =
match arch with
| Architecture.X64 -> "amd64"
| Architecture.Arm64 -> "arm64"
| _ -> invalidOp "unsupported architecture"
for name in [ "mc-server-runner"; "mc-monitor" ] do
let source = Path.Combine(p.Data, "tools", tools, name)
let path = Path.Combine(p.Data, name)
File.Copy(source, path, true)
File.SetUnixFileMode(path, enum<UnixFileMode> 0o755)
let properties = File.ReadAllText(Path.Combine(p.Files, "server.properties")).TrimEnd()
let password = File.ReadAllText(config.PasswordPath).TrimEnd()
text (Path.Combine(p.Data, "server.properties")) (properties + "\nrcon.password=" + password + "\n")
File.Copy(Path.Combine(p.Data, config.Args), Path.Combine(p.Data, "server_args.txt"), true)
let args = File.ReadAllText(Path.Combine(p.Data, "settings", "user_jvm_args.txt")).TrimEnd()
text (Path.Combine(p.Data, "user_jvm_args.txt")) (args + "\n-Djavax.net.ssl.trustStore=/data/cacerts\n")
text (Path.Combine(p.Data, "eula.txt")) ("eula=" + config.Eula.ToLowerInvariant() + "\n")
printfn "Minecraft bootstrap ready"