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
modrinthModrinthServiceportsPortService
Fields
DefaultBedrockPort
Bedrock's default port. Only a starting point for the search.
public const int DefaultBedrockPort = 19132
Field Value
FloodgateProjectId
Modrinth's id for Floodgate, which lets Bedrock players in without a Java account.
public const string FloodgateProjectId = "floodgate"
Field Value
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
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
typeServerType
Returns
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
typeServerType
Returns
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
configServerConfiglogIProgress<string>ctCancellationToken
Returns
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
configServerConfig
Returns
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
typeServerType
Returns
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
serverPortsIEnumerable<int>Bedrock ports the app's other servers already hold.
accountTunnelsIEnumerable<int>Local ports of the UDP tunnels on the playit account.
logIProgress<string>Where to explain a port that was skipped, or a search that could not look.
Returns
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
allIEnumerable<ServerConfig>exceptServerConfig
Returns
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
configServerConfig
Returns
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
configServerConfigThe server being configured.
publicPortint?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.