mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 07:04:53 +09:00
Add version and connect button to player auth
This commit is contained in:
3 files changed
+111
-18
No files matched your search
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user