Merge pull request #703 from clusterio/feature/connect-to-server

Add connect to server button to web UI using steam launch api
This commit is contained in:
Daniel authored and GitHub committed 2024-12-09 15:17:12 -08:00
commit b093cd136f
4 files changed
+37 -23

No files matched your search

+1 -1
View File
@@ -236,7 +236,7 @@ After completing the installation start up the controller and at least one host
The installer provides the `run-controller` and `run-host` scripts to make this simple.
Once the controller process is running you can log into the web interface which is hosted by default on http://localhost:8080/ (adjust the port number if you changed it), use the admin authentication token provided from the installation to log in.
The basics of setting up a Factorio server from the web interface is to create an instance, assign it to a host and then click start.
The basics of setting up a Factorio server from the web interface is to create an instance, assign it to a host and then click start. You can also use the "Connect via Steam" button to launch Factorio directly and connect to the server using Steam's protocol handler.
### Running via Systemd
+12 -22
View File
@@ -1,6 +1,6 @@
import React from "react";
import { useNavigate } from "react-router-dom";
import { message, Button, Space, Table, Typography } from "antd";
import { message, Button, Table } from "antd";
import CopyOutlined from "@ant-design/icons/CopyOutlined";
import type { SizeType } from "antd/es/config-provider/SizeContext";
import type { ColumnsType } from "antd/es/table";
@@ -9,13 +9,14 @@ import { useAccount } from "../model/account";
import { useHosts } from "../model/host";
import InstanceStatusTag from "./InstanceStatusTag";
import StartStopInstanceButton from "./StartStopInstanceButton";
import { InstanceDetails, integerFactorioVersion } from "@clusterio/lib";
import * as lib from "@clusterio/lib";
import Link from "./Link";
import { instancePublicAddress } from "../util/instance";
const strcmp = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }).compare;
type InstanceListProps = {
instances: ReadonlyMap<number, Readonly<InstanceDetails>>;
instances: ReadonlyMap<number, Readonly<lib.InstanceDetails>>;
size?: SizeType;
hideAssignedHost?: boolean;
};
@@ -32,28 +33,14 @@ export default function InstanceList(props: InstanceListProps) {
return hosts.get(hostId)?.name ?? String(hostId);
}
function instancePublicAddress(instance: InstanceDetails) {
if (instance.assignedHost === undefined) {
return "";
}
let host = hosts.get(instance.assignedHost);
if (!host || !host.publicAddress) {
return "";
}
if (instance.gamePort === undefined) {
return host.publicAddress;
}
return `${host.publicAddress}:${instance.gamePort}`;
}
function integerFactorioVersionOrDefault(instance: InstanceDetails) {
function integerFactorioVersionOrDefault(instance: lib.InstanceDetails) {
if (instance.factorioVersion === undefined) {
return -1;
}
return integerFactorioVersion(instance.factorioVersion);
return lib.integerFactorioVersion(instance.factorioVersion);
}
let columns: ColumnsType<InstanceDetails> = [
let columns: ColumnsType<lib.InstanceDetails> = [
{
title: "Name",
dataIndex: "name",
@@ -76,7 +63,7 @@ export default function InstanceList(props: InstanceListProps) {
title: "Public address",
key: "publicAddress",
render: (_, instance) => {
let publicAddress = instancePublicAddress(instance);
let publicAddress = instancePublicAddress(instance, hosts.get(instance.assignedHost!) ?? null);
return publicAddress ? <>
{publicAddress}
<Button
@@ -90,7 +77,10 @@ export default function InstanceList(props: InstanceListProps) {
/>
</> : "";
},
sorter: (a, b) => strcmp(instancePublicAddress(a), instancePublicAddress(b)),
sorter: (a, b) => strcmp(
instancePublicAddress(a, hosts.get(a.assignedHost!) ?? null),
instancePublicAddress(b, hosts.get(b.assignedHost!) ?? null)
),
responsive: ["lg"],
},
{
@@ -23,6 +23,7 @@ import { useInstance } from "../model/instance";
import { useHost } from "../model/host";
import InstanceStatusTag from "./InstanceStatusTag";
import Link from "./Link";
import { instancePublicAddress } from "../util/instance";
type MenuItem = Required<MenuProps>["items"][number];
const { Title } = Typography;
@@ -73,6 +74,7 @@ function InstanceButtons(props: { instance: lib.InstanceDetails }) {
let [exportingData, setExportingData] = useState(false);
let instance = props.instance;
let instanceId = instance.id!;
let [host] = useHost(instance.assignedHost);
let instanceButtonMenuItems: MenuItem[] = [];
if (account.hasPermission("core.instance.export_data")) {
@@ -156,6 +158,17 @@ function InstanceButtons(props: { instance: lib.InstanceDetails }) {
account.hasAnyPermission("core.instance.start", "core.instance.stop")
&& <StartStopInstanceButton instance={instance} />
}
<Button
onClick={() => {
const address = instancePublicAddress(instance, host);
if (address) {
window.location.href = `steam://run/427520//--mp-connect=${address}`;
}
}}
disabled={instance.status !== "running" || !instancePublicAddress(instance, host)}
>
Connect via Steam
</Button>
{account.hasPermission("core.instance.load_scenario") && <LoadScenarioModal instance={instance} />}
{account.hasAnyPermission(
"core.instance.export_data",
+11
View File
@@ -0,0 +1,11 @@
import * as lib from "@clusterio/lib";
export function instancePublicAddress(instance: lib.InstanceDetails, host?: lib.HostDetails | null) {
if (instance.assignedHost === undefined || !host || !host.publicAddress) {
return "";
}
if (instance.gamePort === undefined) {
return host.publicAddress;
}
return `${host.publicAddress}:${instance.gamePort}`;
}