feat: initial commit
This commit is contained in:
commit
623d112dbd
9 changed files with 19563 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/.direnv
|
||||||
|
result
|
56
fabric-server.nix
Normal file
56
fabric-server.nix
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
{ lib
|
||||||
|
, callPackage
|
||||||
|
, fetchurl
|
||||||
|
, writeShellApplication
|
||||||
|
|
||||||
|
, udev
|
||||||
|
, jdk_headless
|
||||||
|
|
||||||
|
, minecraftVersion
|
||||||
|
, fabricLoaderVersion
|
||||||
|
|
||||||
|
, serverJarUrl
|
||||||
|
, serverJarSha1
|
||||||
|
|
||||||
|
, mainClass
|
||||||
|
, libraries
|
||||||
|
|
||||||
|
, extraJvmArgs ? []
|
||||||
|
, mods ? []
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
fetchLib = library: fetchurl {
|
||||||
|
url = library.url;
|
||||||
|
hash = library.sha256;
|
||||||
|
};
|
||||||
|
officialServerJar = fetchurl {
|
||||||
|
url = serverJarUrl;
|
||||||
|
sha1 = serverJarSha1;
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
sourceProvenance = with sourceTypes; [ binaryBytecode ];
|
||||||
|
license = licenses.unfreeRedistributable;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
classPath = lib.concatStringsSep ":" (map fetchLib libraries);
|
||||||
|
modList = lib.concatStringsSep ":" mods;
|
||||||
|
in
|
||||||
|
|
||||||
|
writeShellApplication {
|
||||||
|
name = "fabric-server";
|
||||||
|
|
||||||
|
runtimeInputs = [ jdk_headless ];
|
||||||
|
|
||||||
|
text = ''
|
||||||
|
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${lib.makeLibraryPath [udev]}
|
||||||
|
export LD_LIBRARY_PATH
|
||||||
|
|
||||||
|
exec java -Dfabric.gameJarPath=${officialServerJar} \
|
||||||
|
-Dfabric.addMods=${modList} \
|
||||||
|
${lib.escapeShellArgs extraJvmArgs} \
|
||||||
|
-cp ${classPath} \
|
||||||
|
${mainClass} \
|
||||||
|
nogui
|
||||||
|
'';
|
||||||
|
}
|
30
fabric-servers.nix
Normal file
30
fabric-servers.nix
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{ callPackage
|
||||||
|
, lib
|
||||||
|
|
||||||
|
, javaPackages
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
versions = lib.importJSON ./versions.json;
|
||||||
|
|
||||||
|
escapeVersion = builtins.replaceStrings [ "." ] [ "_" ];
|
||||||
|
getJavaVersion = v: (builtins.getAttr "openjdk${toString v}" javaPackages.compiler).headless;
|
||||||
|
|
||||||
|
packages = lib.mapAttrs'
|
||||||
|
(version: value: {
|
||||||
|
name = escapeVersion version;
|
||||||
|
value = callPackage ./fabric-server.nix {
|
||||||
|
inherit (value) mainClass libraries;
|
||||||
|
inherit (versions) fabricLoaderVersion;
|
||||||
|
|
||||||
|
minecraftVersion = version;
|
||||||
|
|
||||||
|
jdk_headless = getJavaVersion value.vanillaJar.javaVersion;
|
||||||
|
|
||||||
|
serverJarUrl = value.vanillaJar.url;
|
||||||
|
serverJarSha1 = value.vanillaJar.sha1;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
versions.versions;
|
||||||
|
in
|
||||||
|
lib.recurseIntoAttrs packages
|
61
flake.lock
Normal file
61
flake.lock
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710146030,
|
||||||
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1721016451,
|
||||||
|
"narHash": "sha256-Cypl9ORr5UjtBsbjXMTJRepTe362yNVrPrntUvHiTaw=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "a14c5d651cee9ed70f9cd9e83f323f1e531002db",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
31
flake.nix
Normal file
31
flake.nix
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
{
|
||||||
|
overlays.default = final: prev: {
|
||||||
|
inherit (self.packages.${prev.system}) fabric-servers;
|
||||||
|
};
|
||||||
|
lib = import ./lib.nix;
|
||||||
|
} // flake-utils.lib.eachDefaultSystem(system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
};
|
||||||
|
|
||||||
|
scriptDeps = ps: with ps; [
|
||||||
|
requests
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages.fabric-servers = pkgs.callPackage ./fabric-servers.nix {};
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
(python3.withPackages scriptDeps)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
140
gen_versions.py
Normal file
140
gen_versions.py
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import base64
|
||||||
|
import functools
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
V2_VERSIONS_GAME = 'https://meta.fabricmc.net/v2/versions/game'
|
||||||
|
V2_VERSIONS_LOADER_SERVER_JSON = 'https://meta.fabricmc.net/v2/versions/loader/{game_version}/{loader_version}/server/json'
|
||||||
|
LAUNCHER_MANIFEST = 'https://launchermeta.mojang.com/mc/game/version_manifest_v2.json'
|
||||||
|
EXPERIMENTAL_LAUNCHER_MANIFEST = 'https://maven.fabricmc.net/net/minecraft/experimental_versions.json'
|
||||||
|
|
||||||
|
LOADER_VERSION = '0.16.7'
|
||||||
|
|
||||||
|
def fetch_game_versions():
|
||||||
|
print('Fetching game versions...')
|
||||||
|
resp = requests.get(V2_VERSIONS_GAME)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
def fetch_server_profile(game_version: str, loader_version: str):
|
||||||
|
print(' Fetching server profile...')
|
||||||
|
resp = requests.get(V2_VERSIONS_LOADER_SERVER_JSON.format(game_version=game_version, loader_version=loader_version))
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
def format_maven_url(base: str, name: str, extension: str='jar') -> str:
|
||||||
|
parts = name.split(':')
|
||||||
|
if len(parts) == 3:
|
||||||
|
group, name, version = parts
|
||||||
|
else:
|
||||||
|
raise RuntimeError(f'invalid maven object name: `{name}`')
|
||||||
|
group = urllib.parse.quote(group).replace('.', '/')
|
||||||
|
name = urllib.parse.quote(name)
|
||||||
|
filename = urllib.parse.quote(f'{name}-{version}.{extension}')
|
||||||
|
version = urllib.parse.quote(version)
|
||||||
|
return base + group + '/' + name + '/' + version + '/' + filename
|
||||||
|
|
||||||
|
def fetch_launcher_manifest(manifest: str):
|
||||||
|
resp = requests.get(manifest)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
def merge_launcher_manifests():
|
||||||
|
launcher_manifest = fetch_launcher_manifest(LAUNCHER_MANIFEST)
|
||||||
|
experimental_launcher_manifest = fetch_launcher_manifest(EXPERIMENTAL_LAUNCHER_MANIFEST)
|
||||||
|
full_launcher_manifest = {}
|
||||||
|
for version in launcher_manifest['versions']:
|
||||||
|
full_launcher_manifest[version['id']] = version
|
||||||
|
for version in experimental_launcher_manifest['versions']:
|
||||||
|
full_launcher_manifest[version['id']] = version
|
||||||
|
return full_launcher_manifest
|
||||||
|
|
||||||
|
def create_jar_name(maven_url: str) -> str:
|
||||||
|
return re.sub(r'[^a-zA-Z0-9-_.]', '__', maven_url)
|
||||||
|
|
||||||
|
def make_nix_hash(alg: str, hexdigest: str) -> str:
|
||||||
|
b = bytes.fromhex(hexdigest)
|
||||||
|
b64 = base64.b64encode(b).decode()
|
||||||
|
return f'{alg}-{b64}'
|
||||||
|
|
||||||
|
@functools.cache
|
||||||
|
def library_info(baseUrl: str, mavenName: str, sha256: str):
|
||||||
|
url = format_maven_url(baseUrl, mavenName)
|
||||||
|
name = create_jar_name(url)
|
||||||
|
if sha256:
|
||||||
|
return {
|
||||||
|
'url': url,
|
||||||
|
'name': name,
|
||||||
|
'sha256': make_nix_hash('sha256', sha256),
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
print(f' Fetching library hash for {mavenName}...')
|
||||||
|
# we need to fetch the hash
|
||||||
|
hashUrl = url + '.sha256'
|
||||||
|
resp = requests.get(hashUrl)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return {
|
||||||
|
'url': url,
|
||||||
|
'name': name,
|
||||||
|
'sha256': make_nix_hash('sha256', resp.text),
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_libraries(libraries):
|
||||||
|
return list(map(lambda lib: library_info(lib['url'], lib['name'], lib['sha256'] if 'sha256' in lib else None), libraries))
|
||||||
|
|
||||||
|
def generate_version_info(game_version: str, loader_version: str):
|
||||||
|
profile = fetch_server_profile(game_version, loader_version)
|
||||||
|
return {
|
||||||
|
'id': profile['id'],
|
||||||
|
'mainClass': profile['mainClass'],
|
||||||
|
'libraries': get_libraries(profile['libraries']),
|
||||||
|
}
|
||||||
|
|
||||||
|
def main():
|
||||||
|
versions = fetch_game_versions()
|
||||||
|
|
||||||
|
launcher_manifest = merge_launcher_manifests()
|
||||||
|
|
||||||
|
@functools.cache
|
||||||
|
def get_server_jar(version: str):
|
||||||
|
print(f' Fetching server JAR information for {version}...')
|
||||||
|
resp = requests.get(launcher_manifest[version]['url'])
|
||||||
|
resp.raise_for_status()
|
||||||
|
resp = resp.json()
|
||||||
|
server = resp['downloads']['server']
|
||||||
|
javaVersion = '8'
|
||||||
|
if 'javaVersion' in resp and 'majorVersion' in resp['javaVersion']:
|
||||||
|
javaVersion = resp['javaVersion']['majorVersion']
|
||||||
|
return {
|
||||||
|
'url': server['url'],
|
||||||
|
'sha1': server['sha1'],
|
||||||
|
'javaVersion': javaVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
output_versions = {}
|
||||||
|
latestStable = None
|
||||||
|
latestUnstable = None
|
||||||
|
for version in versions:
|
||||||
|
if not latestStable and version['stable']:
|
||||||
|
latestStable = version['version']
|
||||||
|
elif not latestUnstable and not version['stable']:
|
||||||
|
latestUnstable = version['version']
|
||||||
|
print(f'Fetching data for version {version['version']}...')
|
||||||
|
server_jar = get_server_jar(version['version'])
|
||||||
|
version_data = generate_version_info(version['version'], LOADER_VERSION)
|
||||||
|
version_data['vanillaJar'] = server_jar
|
||||||
|
output_versions[version['version']] = version_data
|
||||||
|
with open('versions.json', 'w') as f:
|
||||||
|
json.dump({
|
||||||
|
'latest': {
|
||||||
|
'stable': latestStable,
|
||||||
|
'unstable': latestUnstable,
|
||||||
|
},
|
||||||
|
'fabricLoaderVersion': LOADER_VERSION,
|
||||||
|
'versions': output_versions,
|
||||||
|
}, f, indent=2)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
6
lib.nix
Normal file
6
lib.nix
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
fetchModrinth = { projectId, versionId, fileName, sha256 }: builtins.fetchurl {
|
||||||
|
url = "https://cdn.modrinth.com/data/${projectId}/versions/${versionId}/${fileName}";
|
||||||
|
inherit sha256;
|
||||||
|
};
|
||||||
|
}
|
19236
versions.json
Normal file
19236
versions.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue