Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: geysermc.org plugins (floodgate and geyser) for allowing bedrock clients #68

Open
RonnyPfannschmidt opened this issue Jun 11, 2024 · 6 comments
Labels
enhancement New feature or request new package Requests or adds a new package package Topic: Deals with nix-minecraft packages

Comments

@RonnyPfannschmidt
Copy link

https://geysermc.org has mods/tools to enable a java edition server to also accept bedrock clients
i'd love to easily use it

@Infinidoge
Copy link
Owner

Hmm. Generally I avoid packaging mods and plugins, due to version churn and the sheer scope. However Geyser and Floodgate are good contenders, since a Geyser standalone module would very much make sense here.

@Infinidoge Infinidoge added the enhancement New feature or request label Jun 11, 2024
@RonnyPfannschmidt
Copy link
Author

i did a little more research

https://download.geysermc.org/docs/swagger-ui/index.html has nice tools for the updates as well

geyser can be added as option to all server kinds and has pre-defined builds

when opening the firewall for it as well it would be perfect

im testing a local build of it atm, once it works i'l try to upstream

@Infinidoge Infinidoge added package Topic: Deals with nix-minecraft packages new package Requests or adds a new package labels Aug 8, 2024
@devoxel
Copy link

devoxel commented Dec 2, 2024

@RonnyPfannschmidt any update here? Considering trying this myself

@Infinidoge
Copy link
Owner

I don't currently have the time to figure out packaging this myself, so contributions are welcome.

@RonnyPfannschmidt
Copy link
Author

i currently have a own little script that generates the urls of current plugin versions
i haven't made the time to correctly upstream them

# minectaft-plugins.nix excerpt
{ pkgs, ... }:
{
  # Hangar
  viaversion = pkgs.fetchurl {
    url = "https://hangarcdn.papermc.io/plugins/ViaVersion/ViaVersion/versions/5.0.5/PAPER/ViaVersion-5.0.5.jar";
    hash = "sha256-SXU8E8b8xgMhskXPMmdwNodB3HgGNyet/rEtoqbCAWQ=";
    name = "ViaVersion.jar";
  };
  viabackwards = pkgs.fetchurl {
    url = "https://hangarcdn.papermc.io/plugins/ViaVersion/ViaBackwards/versions/5.0.4/PAPER/ViaBackwards-5.0.4.jar";
    hash = "sha256-v0Pq4pPsQv7+MSo3OZIUYWJC+rD9POy8NCIbvu9QD6M=";
    name = "ViaBackwards.jar";
  };
  # Geyser
  geyser = pkgs.fetchurl {
    url = "https://download.geysermc.org/v2/projects/geyser/versions/2.4.4/builds/694/downloads/spigot";
    hash = "sha256-Nu2IyiA+vLZERnzODJ6NO6U/oPqPsYxEMEgxGKd3hPM=";
    name = "geyser.jar";
  };
  floodgate = pkgs.fetchurl {
    url = "https://download.geysermc.org/v2/projects/floodgate/versions/2.2.3/builds/112/downloads/spigot";
    hash = "sha256-xHKOvGbnBFaaTWq+7KR35FVaBHrw+Lzk6oJhOrvD/ro=";
    name = "floodgate.jar";
  };
}
{ pkgs, ... }:
let
  # python scripts/fetch-downloads.py > machines/rpi4/minecraft-plugins.nix 
  p = import ./minecraft-plugins.nix { pkgs = pkgs; };
  serverpkg = pkgs.paperServers.paper-1_21_1;
 


datapack = pkgs.fetchurl {
    url = "https://cdn.modrinth.com/data/UKUcgltR/versions/NuFJFG6m/PK_Waystones_V.3.4.0_MC_1.21.3.zip";
     sha512 = "b7a758160afeac4bf441054195b2e89a9125e8a06cb42e5169df3a6d1153c8ceb7d3a73f947a8ffa0c53774ae81b31117bf8aefafd6bbf455620a559592e09f7";
     };
in

{

  networking.firewall.allowedTCPPorts = [ 19132 ];
  networking.firewall.allowedUDPPorts = [ 19132 ];

  services.minecraft-servers = {
    enable = true;
    eula = true;

    managementSystem.tmux.enable=false;
    managementSystem.systemd-socket.enable=true;

    servers.paper = {
      enable = true;
      openFirewall = true;
      jvmOpts = "-Xms1024M -Xmx1024M -XX:+AlwaysPreTouch -XX:+ParallelRefProcEnabled -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1HeapRegionSize=4M -XX:MaxInlineLevel=15"; # Avoid OOM
      package = serverpkg;
      serverProperties = {
        server-port = 25565;
        pvp = false;
        online-mode = true;
        level-name = "TheCherries";
        # white-list =
        allow-flight = true;
        enable-command-block = true;
        enforce-secure-profile = false;
        debug = true;
      };
      symlinks = {
        # To avoid internet access
        "cache/mojang_${serverpkg.version}.jar" = serverpkg.vanillaJar;

        "plugins/ViaVersion.jar" = p.viaversion;
        "plugins/ViaBackwards.jar" = p.viabackwards;
        "plugins/geysermc.jar" = p.geyser;
        "plugins/floodgate.jar" = p.floodgate;

      };
      files = {
        "TheCherries/datapacks/waystones.zip" = datapack;


        "plugins/floodgate/config.yml".value = {
          enabled = true;
          use-global-linking = true;
        };
        "plugins/Geyser-Spigot/config.yml".value = {
          remote = {
            auth-type = "floodgate";
          };
          bedrock = {
            servername = "RonnyPfannschmidt";

  
          };
          max-players = 10;
          config-version = 4;
        };
        # A mutable file
        "ops.json".value = [
  # fill your own
        ];
        "whitelist.json".value = [
 # dito
        ];

        "spigot.yml".value = {
          messages.unknown-command = "Unknown command, dummy!";
        };
      };
    };
  };
}


@RonnyPfannschmidt
Copy link
Author

and my hackish WIP script to make the lost of modules

import asyncio
from binascii import b2a_base64, unhexlify
from dataclasses import dataclass, field
from typing import Any, ClassVar

import httpx
from packaging.version import Version
from rich.console import Console


@dataclass
class Forge:
    base_url: ClassVar[str]
    projects_of_interest: ClassVar[tuple[str, ...]]

    _client: httpx.AsyncClient = field(repr=False)

    async def _get(
        self, path: str, params: str | dict[str, str] | None = None
    ) -> httpx.Response:
        full_path = self.base_url + path
        return await self._client.get(full_path, params=params)

    async def _get_json(
        self, path: str, params: str | dict[str, str] | None = None
    ) -> dict[str, Any]:
        response = await self._get(path, params=params)
        response.raise_for_status()
        return response.json()

    def __init__(self, client: httpx.AsyncClient) -> None:
        self._client = client


@dataclass
class ProjectVersion:
    version: str
    url: str
    hash: str
    buildNUmber: int | None = None

    def sort_key(self) -> Version:
        return Version(self.version)


class Hangar(Forge):
    base_url = "https://hangar.papermc.io/api/v1"

    projects_of_interest = (
        "ViaVersion",
        "ViaBackwards",
        "Multiverse-Core",
        "Multiverse-Inventories",
        "Multiverse-Portals",
        "Multiverse-NetherPortals",
        "Multiverse-SignPortals",
        "WorldEdit",
        # "AtomShulkers",
    )

    # "CoreProtect",  external url
    async def get_project_versions(self, name: str) -> list[ProjectVersion]:
        data = await self._get_json(
            f"/projects/{name}/versions",
            params={
                "platform": "paper",
                "platformVersion": "1.21.1",
                "channel": "Release",
            },
        )
        versions: list[ProjectVersion] = []
        for version in data["result"]:
            try:
                versions.append(
                    ProjectVersion(
                        version=version["name"],
                        url=version["downloads"]["PAPER"]["downloadUrl"],
                        hash=hash_2_sri(
                            version["downloads"]["PAPER"]["fileInfo"]["sha256Hash"]
                        ),
                    )
                )
            except Exception as e:
                raise LookupError(name, version["name"]) from e

        versions.sort(key=ProjectVersion.sort_key)
        if not versions:
            raise LookupError(self, name)
        return versions


def hash_2_sri(sha256: str) -> str:
    raw = unhexlify(sha256)
    sri = b2a_base64(raw, newline=False).decode("ascii")
    return f"sha256-{sri}"


class Geyser(Forge):
    base_url = "https://download.geysermc.org/v2"
    projects_of_interest = "geyser", "floodgate"
    download_type = "spigot"

    async def get_project(self, name: str) -> dict[str, Any]:
        return await self._get_json(f"/projects/{name}")

    async def get_project_builds(self, name: str, version: str) -> dict[str, Any]:
        return await self._get_json(f"/projects/{name}/versions/{version}/builds")

    async def get_project_version(self, name: str, version: str):
        builds = await self.get_project_builds(name, version)

        def _build_url(build: dict[str, Any]) -> str:
            return f"{self.base_url}/projects/{name}/versions/{version}/builds/{build['build']}/downloads/{self.download_type}"

        return [
            ProjectVersion(
                version=version,
                url=_build_url(build),
                hash=hash_2_sri(build["downloads"][self.download_type]["sha256"]),
                buildNUmber=build["build"],
            )
            for build in builds["builds"][-1:]
        ]

    async def get_project_versions(self, name: str):
        project = await self.get_project(name)
        res = []
        for version in project["versions"]:
            res.extend(await self.get_project_version(name, version))

        return res


forge_types = [Hangar, Geyser]


async def main():
    console = Console()
    print("{pkgs, ...}: {")
    async with httpx.AsyncClient() as client:
        for kind in forge_types:
            forge = kind(client)
            console.print("#", kind.__name__)
            for project in kind.projects_of_interest:
                lastver = (await forge.get_project_versions(project))[-1]

                console.print(
                    f'  { project.lower() } = pkgs.fetchurl {{ url = {lastver.url}; hash="{lastver.hash}"; name = "{project}.jar"; }};'
                )
    print("}")


asyncio.run(main())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request new package Requests or adds a new package package Topic: Deals with nix-minecraft packages
Projects
None yet
Development

No branches or pull requests

3 participants