Add version and connect button to player auth

This commit is contained in:
Cooldude2606 committed 2026-05-31 23:55:43 +01:00
1 parent f2ca465ec6
commit f2ccde0dd6
3 files changed
+111 -18

No files matched your search

+26 -5
View File
@@ -3,11 +3,12 @@ import express, { type Request, type Response } from "express";
import util from "util";
import jwt from "jsonwebtoken";
import { Static } from "@sinclair/typebox";
import * as lib from "@clusterio/lib";
import { BaseControllerPlugin } from "@clusterio/controller";
const { basicType } = lib;
import { FetchPlayerCodeRequest, SetVerifyCodeRequest } from "./messages";
import { FetchPlayerCodeRequest, PlayerAuthServer, SetVerifyCodeRequest } from "./messages";
async function generateCode(length: number): Promise<string> {
@@ -46,12 +47,32 @@ export class ControllerPlugin extends BaseControllerPlugin {
}, 60e3).unref();
this.controller.app.get("/api/player_auth/servers", (req: Request, res: Response) => {
let servers: string[] = [];
for (let instance of this.controller.instances.values()) {
if (instance.status === "running" && instance.config.get("player_auth.load_plugin")) {
servers.push(instance.config.get("factorio.settings")["name"] as string || "unnamed server");
const servers: Static<typeof PlayerAuthServer.jsonSchema>[] = [];
for (const instance of this.controller.instances.values()) {
const pluginLoaded = instance.config.get("player_auth.load_plugin");
const assignedHost = instance.config.get("instance.assigned_host");
if (instance.status !== "running" || !pluginLoaded || !assignedHost) {
continue;
}
const host = this.controller.hosts.get(assignedHost);
if (!host || !host.publicAddress) {
continue;
}
const address = instance.gamePort !== undefined
? `${host.publicAddress}:${instance.gamePort}`
: host.publicAddress;
const settings = instance.config.get("factorio.settings");
servers.push({
name: settings["name"] as string || "unnamed server",
factorioVersion: instance.factorioVersion,
address,
});
}
res.send(servers);
});
+27
View File
@@ -1,5 +1,32 @@
import { Type, Static } from "@sinclair/typebox";
export class PlayerAuthServer {
declare ["constructor"]: typeof PlayerAuthServer;
static jsonSchema = Type.Object({
name: Type.String(),
address: Type.String(),
factorioVersion: Type.Optional(Type.String()),
});
constructor(
public name: string,
public address: string,
public factorioVersion?: string,
) {}
toJSON(): Static<typeof PlayerAuthServer.jsonSchema> {
if (this.factorioVersion === undefined) {
return { name: this.name, address: this.address };
}
return { name: this.name, address: this.address, factorioVersion: this.factorioVersion };
}
static fromJSON(json: Static<typeof PlayerAuthServer.jsonSchema>) {
return new this(json.name, json.address, json.factorioVersion);
}
}
class FetchPlayerCodeResponse {
constructor(
public playerCode: string,
+58 -13
View File
@@ -3,6 +3,7 @@ import React, { useEffect, useState } from "react";
import { BaseWebPlugin, notifyErrorHandler } from "@clusterio/web_ui";
import { Button, Form, Input, Spin, Typography } from "antd";
import { PlayerAuthServer } from "../messages";
import "./style.css";
const { Paragraph, Text } = Typography;
@@ -12,21 +13,39 @@ interface LoginFormProps {
}
function LoginForm(props: LoginFormProps) {
let [servers, setServers] = useState<string[] | null>(null);
const [servers, setServers] = useState<PlayerAuthServer[] | null>(null);
let [playerCode, setPlayerCode] = useState<string | null>(null);
let [playerCodeError, setPlayerCodeError] = useState<string | null>(null);
let [verifyCode, setVerifyCode] = useState<string | undefined>();
let [verifyToken, setVerifyToken] = useState<string | null>(null);
useEffect(() => {
(async () => {
let cancelled = false;
async function load() {
let response = await fetch(`${webRoot}api/player_auth/servers`);
if (response.ok) {
setServers(await response.json());
} else {
setServers([]);
if (!response.ok) {
if (!cancelled) {
setServers([]);
const err = new Error(await response.text());
notifyErrorHandler("Error retrieving servers")(err);
}
return;
}
})();
const json = await response.json();
if (!cancelled) {
setServers(json.map((s: any) => PlayerAuthServer.fromJSON(s)));
}
}
load();
const interval = setInterval(load, 15 * 1000);
return () => {
cancelled = true;
clearInterval(interval);
};
}, []);
useEffect(() => {
@@ -61,7 +80,7 @@ function LoginForm(props: LoginFormProps) {
clearInterval(checkLoop);
notifyErrorHandler("Error verifying code")(err);
});
}, 2000);
}, 2 * 1000);
return () => { clearInterval(checkLoop); };
}
@@ -74,8 +93,8 @@ function LoginForm(props: LoginFormProps) {
if (servers.length === 0) {
return <Paragraph>
There are no servers in the cluster currently running that can complete
the login via Factorio. Please try again later.
There are no running servers available for login via Factorio at the moment.
Please try again later.
</Paragraph>;
}
@@ -89,9 +108,35 @@ function LoginForm(props: LoginFormProps) {
Start Factorio, join one of the following multiplayer servers
and type <Text keyboard>/web-login</Text> into the chat:
</Paragraph>
<div style={{ maxHeight: 160, overflowY: "auto", paddingLeft: 16 }}>
<ul>
{servers.map(text => <li key={text}>{text}</li>)}
<div style={{ maxHeight: 160, overflowY: "auto", paddingLeft: 16, padding: "8px 0" }}>
<ul style={{ margin: 0, paddingLeft: 16 }}>
{servers.map(server => (
<li key={server.address} style={{ marginBottom: 6 }}>
<div style={{
display: "grid",
gridTemplateColumns: "1fr auto",
alignItems: "center",
gap: 8,
}}>
<span>
{server.name}
{server.factorioVersion && (
<Text type="secondary"> ({server.factorioVersion})</Text>
)}
</span>
<Button
size="small"
onClick={() => {
window.location.href =
`steam://run/427520//--mp-connect=${server.address}`;
}}
>
Connect
</Button>
</div>
</li>
))}
</ul>
</div>
</li>