diff --git a/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json b/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json index a9371a3e..5259df0b 100644 --- a/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json +++ b/database/Seeders/eggs/source-engine/egg-counter--strike--global-offensive.json @@ -8,7 +8,9 @@ "name": "Counter-Strike: Global Offensive", "author": "support@pterodactyl.io", "description": "Counter-Strike: Global Offensive is a multiplayer first-person shooter video game developed by Hidden Path Entertainment and Valve Corporation.", - "features": null, + "features": [ + "gsl_token" + ], "images": [ "ghcr.io\/pterodactyl\/games:source" ], diff --git a/database/Seeders/eggs/source-engine/egg-garrys-mod.json b/database/Seeders/eggs/source-engine/egg-garrys-mod.json index 93de416a..d12e832f 100644 --- a/database/Seeders/eggs/source-engine/egg-garrys-mod.json +++ b/database/Seeders/eggs/source-engine/egg-garrys-mod.json @@ -8,7 +8,9 @@ "name": "Garrys Mod", "author": "support@pterodactyl.io", "description": "Garrys Mod, is a sandbox physics game created by Garry Newman, and developed by his company, Facepunch Studios.", - "features": null, + "features": [ + "gsl_token" + ], "images": [ "ghcr.io\/pterodactyl\/games:source" ], diff --git a/resources/scripts/components/server/ServerConsole.tsx b/resources/scripts/components/server/ServerConsole.tsx index 1c6aca4d..73b06676 100644 --- a/resources/scripts/components/server/ServerConsole.tsx +++ b/resources/scripts/components/server/ServerConsole.tsx @@ -7,7 +7,7 @@ import ServerContentBlock from '@/components/elements/ServerContentBlock'; import ServerDetailsBlock from '@/components/server/ServerDetailsBlock'; import isEqual from 'react-fast-compare'; import PowerControls from '@/components/server/PowerControls'; -import { EulaModalFeature, JavaVersionModalFeature } from '@feature/index'; +import { EulaModalFeature, JavaVersionModalFeature, GSLTokenModalFeature } from '@feature/index'; import ErrorBoundary from '@/components/elements/ErrorBoundary'; import Spinner from '@/components/elements/Spinner'; @@ -60,6 +60,7 @@ const ServerConsole = () => { {eggFeatures.includes('eula') && } {eggFeatures.includes('java_version') && } + {eggFeatures.includes('gsl_token') && } diff --git a/resources/scripts/components/server/features/GSLTokenModalFeature.tsx b/resources/scripts/components/server/features/GSLTokenModalFeature.tsx new file mode 100644 index 00000000..ff70c12f --- /dev/null +++ b/resources/scripts/components/server/features/GSLTokenModalFeature.tsx @@ -0,0 +1,101 @@ +import React, { useEffect, useState } from 'react'; +import { ServerContext } from '@/state/server'; +import Modal from '@/components/elements/Modal'; +import tw from 'twin.macro'; +import Button from '@/components/elements/Button'; +import FlashMessageRender from '@/components/FlashMessageRender'; +import useFlash from '@/plugins/useFlash'; +import { SocketEvent, SocketRequest } from '@/components/server/events'; +import Field from '@/components/elements/Field'; +import updateStartupVariable from '@/api/server/updateStartupVariable'; +import { Form, Formik, FormikHelpers } from 'formik'; + +interface Values { + gslToken: string; +} + +const GSLTokenModalFeature = () => { + const [ visible, setVisible ] = useState(false); + const [ loading, setLoading ] = useState(false); + + const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); + const status = ServerContext.useStoreState(state => state.status.value); + const { clearFlashes, clearAndAddHttpError } = useFlash(); + const { connected, instance } = ServerContext.useStoreState(state => state.socket); + + useEffect(() => { + if (!connected || !instance || status === 'running') return; + + const errors = [ + '(gsl token expired)', + '(account not found)', + ]; + + const listener = (line: string) => { + if (errors.some(p => line.toLowerCase().includes(p))) { + setVisible(true); + } + }; + + instance.addListener(SocketEvent.CONSOLE_OUTPUT, listener); + + return () => { + instance.removeListener(SocketEvent.CONSOLE_OUTPUT, listener); + }; + }, [ connected, instance, status ]); + + const updateGSLToken = (values: Values, { setSubmitting }: FormikHelpers) => { + setLoading(true); + clearFlashes('feature:gslToken'); + + updateStartupVariable(uuid, 'STEAM_ACC', values.gslToken) + .then(() => { + if (instance) { + instance.send(SocketRequest.SET_STATE, 'restart'); + } + + setLoading(false); + setVisible(false); + }) + .catch(error => { + console.error(error); + clearAndAddHttpError({ key: 'feature:gslToken', error }); + }) + .then(() => setLoading(false)); + }; + + useEffect(() => { + clearFlashes('feature:gslToken'); + }, []); + + return ( + + setVisible(false)} closeOnBackground={false} showSpinnerOverlay={loading}> + +
+

Invalid GSL token!

+

It seems like your Gameserver Login Token (GSL token) is invalid or has expired.

+

You can either generate a new one and enter it below or leave the field blank to remove it completely.

+
+ +
+
+ +
+
+
+
+ ); +}; + +export default GSLTokenModalFeature; diff --git a/resources/scripts/components/server/features/index.ts b/resources/scripts/components/server/features/index.ts index d59a59cb..c84d5718 100644 --- a/resources/scripts/components/server/features/index.ts +++ b/resources/scripts/components/server/features/index.ts @@ -8,5 +8,6 @@ import { lazy } from 'react'; */ const EulaModalFeature = lazy(() => import(/* webpackChunkName: "feature.eula" */'@feature/eula/EulaModalFeature')); const JavaVersionModalFeature = lazy(() => import(/* webpackChunkName: "feature.java_version" */'@feature/JavaVersionModalFeature')); +const GSLTokenModalFeature = lazy(() => import(/* webpackChunkName: "feature.gsl_token" */'@feature/GSLTokenModalFeature')); -export { EulaModalFeature, JavaVersionModalFeature }; +export { EulaModalFeature, JavaVersionModalFeature, GSLTokenModalFeature };