Create lobbies on demand and only list servers on the player's version

When a player connects with a Factorio version and set of built in mods
no lobby runs, the lobby plugin now creates one on a connected host that
has the version installed or can download it, with a mod pack matching
the player's built in mods. This lets a cluster accept players on
several Factorio versions on the same port without any setup. It can be
turned off with lobby.auto_create.

To pick the mod pack the proxy now waits for the whole connection
confirm before routing and passes the mods listed in it to the
playerProxyRoute hook. Hosts answer the new HostFactorioVersionsRequest
with the versions they have installed.

The server list in lobbies only shows instances on the Factorio version
of the lobby, with a checkbox to also show the others greyed out.
This commit is contained in:
Danielv123 committed 2026-10-29 20:54:11 +01:00
1 parent a5bd43637e
commit 00823262d5
14 files changed
+582 -39

No files matched your search

+48
View File
@@ -267,6 +267,54 @@ export function parseConnectionRequestReplyConfirm(buffer: Buffer): ConnectionRe
return { clientRequestId, serverRequestId, serverRequestIdOffset, username };
}
/** Mod enabled in a Factorio client */
export interface ModId {
name: string;
version: string;
}
/**
* Join the payloads of the fragments of a message
*
* @param fragments -
* Fragments of the message in order of fragment number, or the message
* if it's not fragmented.
* @returns the payload of the message.
*/
export function joinPayload(fragments: Buffer[]) {
return Buffer.concat(fragments.map(fragment => fragment.subarray(parseHeader(fragment).payloadOffset)));
}
/**
* Read the mods enabled in the client from a ConnectionRequestReplyConfirm
*
* @param payload - Complete payload of the message, see {@link joinPayload}.
* @returns name and version of each mod.
*/
export function parseConfirmMods(payload: Buffer): ModId[] {
const reader = new BufferReader(payload);
reader.readUInt32LE(); // connectionRequestIDGeneratedOnClient
reader.readUInt32LE(); // connectionRequestIDGeneratedOnServer
reader.readUInt32LE(); // instanceID
reader.readString(); // username
reader.readString(); // password
reader.readString(); // serverKey
reader.readString(); // serverKeyTimestamp
reader.readUInt32LE(); // coreChecksum
reader.readUInt32LE(); // prototypeListChecksum
const count = reader.readSpaceOptimizedUInt32();
const mods: ModId[] = [];
for (let i = 0; i < count; i++) {
const name = reader.readString();
const version = [
reader.readSpaceOptimizedUInt16(), reader.readSpaceOptimizedUInt16(), reader.readSpaceOptimizedUInt16(),
].join(".");
reader.readUInt32LE(); // crc
mods.push({ name, version });
}
return mods;
}
export function encodeConnectionRequest(version: ApplicationVersion, clientRequestId: number, messageId = 0) {
const writer = new BufferWriter();
writer.writeUInt8(MessageType.ConnectionRequest);