Table of Contents

Class CrossplayService

Namespace
McServerLauncher.Services
Assembly
McServerLauncher.dll

Setting a server up so people can join from Bedrock as well as Java.

public class CrossplayService
Inheritance
CrossplayService
Inherited Members

Remarks

Three separate things have to line up, which is why doing this by hand goes wrong. The server needs Geyser to understand Bedrock clients at all, and Floodgate so those players don't each need to own Minecraft: Java. It needs a second tunnel, because Java is TCP and Bedrock is UDP and one cannot carry the other. And Geyser has to be told the tunnel's public port, or the Bedrock server list advertises a port nobody can use.

Geyser comes from Modrinth through the app's existing install path, which already resolves the right build for the loader and version and verifies its hash. Floodgate is split: Modrinth carries the Fabric and NeoForge builds, and only GeyserMC's own downloads site carries the Spigot one that Paper needs. Whichever the source, nothing is installed without its checksum.

Constructors

CrossplayService(ModrinthService?, PortService?)

public CrossplayService(ModrinthService? modrinth = null, PortService? ports = null)

Parameters

modrinth ModrinthService
ports PortService

Fields

DefaultBedrockPort

Bedrock's default port. Only a starting point for the search.

public const int DefaultBedrockPort = 19132

Field Value

int

FloodgateProjectId

Modrinth's id for Floodgate, which lets Bedrock players in without a Java account.

public const string FloodgateProjectId = "floodgate"

Field Value

string

Remarks

Only usable for Fabric and NeoForge. Modrinth's "floodgate" project is GeyserMC/Floodgate-Modded, which is exactly those two loaders — the Spigot build that Paper needs is published only on GeyserMC's own downloads site, so Paper takes the other path below. Assuming one source covered all three was wrong, and it would have failed on the server type most people would pick for this.

GeyserProjectId

Modrinth's id for Geyser: the Bedrock↔Java translator.

public const string GeyserProjectId = "geyser"

Field Value

string

Methods

CanEnable(ServerType)

Whether this server can do crossplay at all, and why not when it can't.

public static bool CanEnable(ServerType type)

Parameters

type ServerType

Returns

bool

CaveatKey(ServerType)

The resx key of the caveat to show beside the crossplay checkbox, or null when there is none.

public static string? CaveatKey(ServerType type)

Parameters

type ServerType

Returns

string

Remarks

Two different caveats, because the two mod loaders are in genuinely different positions and one paragraph covering both said less about each. Fabric has an answer — the content checkbox — and NeoForge does not, so its note says what to expect instead of what to tick. The plugin types get nothing: there is no caveat, and inventing one would be noise.

InstallAsync(ServerConfig, IProgress<string>?, CancellationToken)

Downloads Geyser and Floodgate into the server, verified, and reports progress.

public Task InstallAsync(ServerConfig config, IProgress<string>? log, CancellationToken ct = default)

Parameters

config ServerConfig
log IProgress<string>
ct CancellationToken

Returns

Task

Remarks

Both or neither: a server with Geyser but no Floodgate starts and accepts Bedrock connections, then rejects every player who hasn't bought Minecraft: Java — which looks like crossplay is broken rather than half-installed. Failing loudly is the better outcome.

IsFloodgateInstalled(ServerConfig)

Whether a Floodgate jar is sitting in the server's content folder.

public static bool IsFloodgateInstalled(ServerConfig config)

Parameters

config ServerConfig

Returns

bool

Remarks

Asked of the folder rather than remembered in the config: someone can delete the jar by hand, and telling Geyser to authenticate against a Floodgate that is not there turns every Bedrock player away.

ModsCanLockOutBedrock(ServerType)

Whether the mods installed on this kind of server can shut Bedrock players out.

public static bool ModsCanLockOutBedrock(ServerType type)

Parameters

type ServerType

Returns

bool

Remarks

True for the mod loaders, false for Paper. Geyser joins the Java server as a client with no mods, and a mod loader carrying content the client is required to have refuses exactly that connection — so crossplay works the day it is switched on and stops working the day a mod adding blocks or items is installed, with nothing linking the two events.

Paper is not affected because plugins run only on the server; a client with no plugins is the only kind there is. Which is why it is the honest recommendation for wanting both content and Bedrock players.

PickBedrockPort(IEnumerable<int>, IEnumerable<int>, IProgress<string>?)

A free local UDP port for Geyser, starting at Bedrock's default.

public int PickBedrockPort(IEnumerable<int> serverPorts, IEnumerable<int> accountTunnels, IProgress<string>? log = null)

Parameters

serverPorts IEnumerable<int>

Bedrock ports the app's other servers already hold.

accountTunnels IEnumerable<int>

Local ports of the UDP tunnels on the playit account.

log IProgress<string>

Where to explain a port that was skipped, or a search that could not look.

Returns

int

Remarks

Asked of the UDP table, not the TCP one. They are separate namespaces, so a port can be taken for TCP and free for UDP — and picking with the wrong table hands out a port something else already holds, which shows up only as a Geyser that fails to bind.

Three sources, because two were not enough. The UDP table only knows what is bound right now, so a stopped server's port looks free; the other servers' ports cover the ones this app knows about; and accountTunnels covers the rest — a tunnel left behind by a deleted server, one made by hand, or one belonging to another machine on the same playit account. Without that third list the port looks free, and creating a tunnel on it silently adopts somebody else's.

PortsHeldBy(IEnumerable<ServerConfig>, ServerConfig)

The Bedrock ports the other servers already hold, for PickBedrockPort(IEnumerable<int>, IEnumerable<int>, IProgress<string>?).

public static IEnumerable<int> PortsHeldBy(IEnumerable<ServerConfig> all, ServerConfig except)

Parameters

all IEnumerable<ServerConfig>
except ServerConfig

Returns

IEnumerable<int>

Remarks

Here rather than inside the view model that used to hold it, because it is a rule about ports and not about a list on screen — and because a rule that decides which port a server gets should be provable without building a window. Its three parts each fix a real way to hand out a port twice: skip the server being set up (it is allowed to keep its own port), drop the zeros (unset, not a port), and drop duplicates.

Compared by reference to except, not by name or folder: two servers may share either while being different servers, and the one thing that is certainly unique is the object itself.

RepairConfig(ServerConfig)

Corrects a Geyser config the app wrote badly, and says what it changed. Null when nothing did.

public string? RepairConfig(ServerConfig config)

Parameters

config ServerConfig

Returns

string

Remarks

Existing servers need this, not just new ones: a config written before this was understood sits on auth-type: online for ever, Geyser tries to authenticate against Mojang with no account, and every Bedrock player is turned away with Floodgate asking whether it is configured correctly. Nothing about that resolves itself.

Only the settings the app itself is responsible for are touched, and the file is left alone when they already say the right thing — so running it on every start costs nothing and cannot fight a config somebody fixed by hand.

WriteConfig(ServerConfig, int?)

Writes Geyser's local port and, when the server is tunnelled, the public port it must advertise. Safe to call repeatedly: it patches what is there rather than replacing it.

public void WriteConfig(ServerConfig config, int? publicPort)

Parameters

config ServerConfig

The server being configured.

publicPort int?

The tunnel's public port, or null when there is no tunnel — on a local network the port players use is the local one, and advertising anything else would be wrong.