From 339ce9c3690d6f4f88a2862c03a3ea3d111de656 Mon Sep 17 00:00:00 2001 From: Alex Anderson Date: Tue, 24 May 2022 13:32:21 -0400 Subject: [PATCH] fix(Networking): Overhaul the networking layer to be more flexible for card and core development. --- client/src/cards/Login/index.tsx | 4 +- .../cards/OfficersLog/OfficersLog.test.tsx | 10 +- client/src/cards/OfficersLog/data.tsx | 8 +- client/src/cards/OfficersLog/index.tsx | 6 +- client/src/cards/Offline/index.tsx | 4 +- client/src/cards/Pilot/data.tsx | 17 +- client/src/cards/Pilot/index.tsx | 10 +- client/src/cards/clientData.ts | 47 ++-- client/src/cards/timer/data.ts | 2 +- client/src/components/ClientButton.tsx | 7 +- .../FlightDirector/CoreFlexLayoutDropdown.tsx | 1 + .../src/components/FlightQuickStart/index.tsx | 9 +- client/src/components/Station/CardArea.tsx | 9 +- .../src/components/Station/CardSwitcher.tsx | 4 +- .../src/components/Station/StationLayout.tsx | 11 +- client/src/components/Station/index.tsx | 6 +- .../src/components/Station/useManageCard.tsx | 4 +- client/src/components/WelcomeButtons.tsx | 10 +- client/src/components/WelcomeLogo.tsx | 6 +- client/src/context/AppContext.tsx | 14 ++ client/src/context/SocketHandler.tsx | 27 +-- client/src/context/useCardData.ts | 62 ----- client/src/context/useDataStream.tsx | 42 ++++ client/src/context/useNetRequest.ts | 12 +- client/src/context/useRequestSub.tsx | 2 +- client/src/cores/LoginCore/data.tsx | 4 +- client/src/cores/LoginCore/index.tsx | 6 +- client/src/pages/CardRenderer.tsx | 6 +- .../src/pages/Config/Themes/ThemeLayout.tsx | 16 +- client/src/pages/Config/index.tsx | 6 +- client/src/pages/FlightLobby.tsx | 31 ++- client/src/utils/cardData.ts | 46 ++-- client/src/utils/clientSocket.ts | 1 + client/test-utils.tsx | 33 +-- client/vite.config.ts | 1 - server/nodemon.json | 2 + server/src/classes/Client.ts | 211 ++++++------------ server/src/init/setUpAPI.ts | 102 +-------- server/src/netRequests/index.ts | 32 ++- server/src/utils/pubsub.ts | 15 +- 40 files changed, 344 insertions(+), 502 deletions(-) delete mode 100644 client/src/context/useCardData.ts create mode 100644 client/src/context/useDataStream.tsx diff --git a/client/src/cards/Login/index.tsx b/client/src/cards/Login/index.tsx index 292a79e7..252cda1e 100644 --- a/client/src/cards/Login/index.tsx +++ b/client/src/cards/Login/index.tsx @@ -1,13 +1,13 @@ import Button from "@thorium/ui/Button"; import Input from "@thorium/ui/Input"; import {netSend} from "client/src/context/netSend"; -import {useClientData} from "client/src/context/useCardData"; +import {useNetRequest} from "client/src/context/useNetRequest"; import {useState} from "react"; const Login = () => { const [loginName, setLoginName] = useState(""); // TODO: Support logging in with a ThoriumSim account - const {ship} = useClientData(); + const ship = useNetRequest("ship"); const login = () => { if (loginName.trim().length > 0) { // TODO: Play a sound effect when the user logs in diff --git a/client/src/cards/OfficersLog/OfficersLog.test.tsx b/client/src/cards/OfficersLog/OfficersLog.test.tsx index a408b853..e65f3592 100644 --- a/client/src/cards/OfficersLog/OfficersLog.test.tsx +++ b/client/src/cards/OfficersLog/OfficersLog.test.tsx @@ -5,9 +5,10 @@ import userEvent from "@testing-library/user-event"; import {act} from "react-dom/test-utils"; test("it should render without error", async () => { - const {findByText, queryByText, findByRole, netSendSpy} = - await render<"OfficersLog">(, { - cardData: { + const {findByText, queryByText, findByRole, netSendSpy} = await render( + , + { + netRequestData: { officersLog: [ { message: "This is a test log entry", @@ -15,7 +16,8 @@ test("it should render without error", async () => { }, ], }, - }); + } + ); const logEl = await findByText("@560.60"); expect(logEl).toBeInTheDocument(); userEvent.click(logEl); diff --git a/client/src/cards/OfficersLog/data.tsx b/client/src/cards/OfficersLog/data.tsx index d9dc520a..58f7c7fd 100644 --- a/client/src/cards/OfficersLog/data.tsx +++ b/client/src/cards/OfficersLog/data.tsx @@ -1,7 +1,11 @@ import {DataContext} from "server/src/utils/DataContext"; -export const subscriptions = { - officersLog(context: DataContext, publishParams: {clientId: string}) { +export const requests = { + officersLog( + context: DataContext, + params: {}, + publishParams: {clientId: string} + ) { if (publishParams && context.clientId !== publishParams.clientId) throw null; diff --git a/client/src/cards/OfficersLog/index.tsx b/client/src/cards/OfficersLog/index.tsx index 0f4bae4a..bbb36c06 100644 --- a/client/src/cards/OfficersLog/index.tsx +++ b/client/src/cards/OfficersLog/index.tsx @@ -1,13 +1,13 @@ import Button from "@thorium/ui/Button"; import {netSend} from "client/src/context/netSend"; import {toast} from "client/src/context/ToastContext"; -import useCardData, {useClientData} from "client/src/context/useCardData"; +import {useNetRequest} from "client/src/context/useNetRequest"; import {fromDate} from "dot-beat-time"; import * as React from "react"; export default function OfficersLog() { - const {client} = useClientData(); - const {officersLog} = useCardData<"OfficersLog">(); + const client = useNetRequest("client"); + const officersLog = useNetRequest("officersLog"); const [stardate, setStardate] = React.useState(new Date()); const [logEntry, setLogEntry] = React.useState(""); const [selectedEntry, setSelectedEntry] = React.useState(); diff --git a/client/src/cards/Offline/index.tsx b/client/src/cards/Offline/index.tsx index da6de4e1..37aa1c53 100644 --- a/client/src/cards/Offline/index.tsx +++ b/client/src/cards/Offline/index.tsx @@ -1,4 +1,4 @@ -import {useClientData} from "client/src/context/useCardData"; +import {useNetRequest} from "client/src/context/useNetRequest"; import {Fragment} from "react"; // Messages go here @@ -26,7 +26,7 @@ const messages: Record = { }; const Offline: React.FC = () => { - const {client} = useClientData(); + const client = useNetRequest("client"); if (!client.offlineState) return null; return ( diff --git a/client/src/cards/Pilot/data.tsx b/client/src/cards/Pilot/data.tsx index 23d593ba..db354f50 100644 --- a/client/src/cards/Pilot/data.tsx +++ b/client/src/cards/Pilot/data.tsx @@ -1,11 +1,20 @@ import {getShipSystem} from "client/src/utils/getShipSystem"; import {DataContext} from "server/src/utils/DataContext"; +import {Entity} from "server/src/utils/ecs"; -export const subscriptions = { - ship(context: DataContext) { - return context.ship?.toJSON(); - }, +export const requests = { impulseEngines(context: DataContext) { return getShipSystem(context, "impulseEngines"); }, }; + +export function dataStream( + entity: Entity, + context: DataContext, + params: {systemId: number | null} +): boolean { + return Boolean( + entity.components.position && + entity.components.position.parentId === params.systemId + ); +} diff --git a/client/src/cards/Pilot/index.tsx b/client/src/cards/Pilot/index.tsx index 77254769..49c0cd5a 100644 --- a/client/src/cards/Pilot/index.tsx +++ b/client/src/cards/Pilot/index.tsx @@ -1,13 +1,17 @@ -import useCardData from "client/src/context/useCardData"; +import {useDataStream} from "client/src/context/useDataStream"; +import {useNetRequest} from "client/src/context/useNetRequest"; export function Pilot() { - const cardData = useCardData<"Pilot">(); + const ship = useNetRequest("ship"); + const impulseEngines = useNetRequest("impulseEngines"); + useDataStream({systemId: null}); return (

Pilot

-        {JSON.stringify(cardData, null, 2)}
+        {JSON.stringify(ship, null, 2)}
+        {JSON.stringify(impulseEngines, null, 2)}
       
); diff --git a/client/src/cards/clientData.ts b/client/src/cards/clientData.ts index 1ec21412..bff7f4ba 100644 --- a/client/src/cards/clientData.ts +++ b/client/src/cards/clientData.ts @@ -7,7 +7,7 @@ import {staticStations} from "server/src/classes/Station"; // This file is used for any subscriptions which all clients // make, regardless of what cards they have. -export const subscriptions = { +export const requests = { thorium: (context: DataContext) => { const hasHost = Object.values(context.server.clients).some( client => client.isHost && client.connected @@ -16,8 +16,13 @@ export const subscriptions = { hasHost, }; }, - client: (context: DataContext, params: {clientId: string}) => { - if (params && params.clientId !== context.clientId) throw null; + client: ( + context: DataContext, + params: {}, + publishParams: {clientId: string} + ) => { + if (publishParams && publishParams.clientId !== context.clientId) + throw null; const {id, name, connected, isHost} = context.server.clients[context.clientId]; @@ -34,17 +39,32 @@ export const subscriptions = { const {date, name, paused} = flight; return {date, name, paused}; }, - ship(context: DataContext, params: {shipId: number} | {clientId: string}) { - if (params) { - if ("shipId" in params && params.shipId !== context.flightClient?.shipId) + ship( + context: DataContext, + params: {}, + publishParams: {shipId: number} | {clientId: string} + ) { + if (publishParams) { + if ( + "shipId" in publishParams && + publishParams.shipId !== context.flightClient?.shipId + ) throw null; - if ("clientId" in params && params.clientId !== context.clientId) + if ( + "clientId" in publishParams && + publishParams.clientId !== context.clientId + ) throw null; } return context.ship?.toJSON() as Entity; }, - station(context: DataContext, params: {clientId: string}): Station { - if (params && params.clientId !== context.clientId) throw null; + station( + context: DataContext, + params: {}, + publishParams: {clientId: string} + ): Station { + if (publishParams && publishParams.clientId !== context.clientId) + throw null; if (context.flightClient?.stationOverride) return context.flightClient.stationOverride; const station = staticStations @@ -54,8 +74,9 @@ export const subscriptions = { ) as unknown as Station; return station || null; }, - theme(context: DataContext, params: {clientId: string}) { - if (params && params.clientId !== context.clientId) throw null; + theme(context: DataContext, params: {}, publishParams: {clientId: string}) { + if (publishParams && publishParams.clientId !== context.clientId) + throw null; const themeObj = context.server.plugins .filter(plugin => context.flight?.pluginIds.includes(plugin.id)) .reduce((acc: null | ThemePlugin, plugin) => { @@ -70,7 +91,3 @@ export const subscriptions = { return themeObj; }, }; - -export function dataStream(entity: Entity, context: DataContext): boolean { - return true; //!!(entity.components.position && entity.components.velocity); -} diff --git a/client/src/cards/timer/data.ts b/client/src/cards/timer/data.ts index 456aa6a4..033f0aef 100644 --- a/client/src/cards/timer/data.ts +++ b/client/src/cards/timer/data.ts @@ -1,6 +1,6 @@ import {DataContext} from "server/src/utils/DataContext"; -export const subscriptions = { +export const requests = { timer(context: DataContext) { // TODO: fetch timer data }, diff --git a/client/src/components/ClientButton.tsx b/client/src/components/ClientButton.tsx index e8252bc7..868f168b 100644 --- a/client/src/components/ClientButton.tsx +++ b/client/src/components/ClientButton.tsx @@ -1,10 +1,11 @@ -import {useClientData} from "../context/useCardData"; import {usePrompt} from "@thorium/ui/AlertDialog"; import Button from "@thorium/ui/Button"; import {netSend} from "../context/netSend"; +import {useNetRequest} from "../context/useNetRequest"; export function ClientButton() { - const clientData = useClientData(); + const client = useNetRequest("client"); + const prompt = usePrompt(); return (
@@ -20,7 +21,7 @@ export function ClientButton() { } }} > - {clientData?.client.name || ""} + {client.name || ""}
); diff --git a/client/src/components/FlightDirector/CoreFlexLayoutDropdown.tsx b/client/src/components/FlightDirector/CoreFlexLayoutDropdown.tsx index 9cb1140c..d98fab49 100644 --- a/client/src/components/FlightDirector/CoreFlexLayoutDropdown.tsx +++ b/client/src/components/FlightDirector/CoreFlexLayoutDropdown.tsx @@ -33,6 +33,7 @@ export function CoreFlexLayoutDropdown() { }, { enabled: account?.user_id !== undefined, + suspense: false, } ); diff --git a/client/src/components/FlightQuickStart/index.tsx b/client/src/components/FlightQuickStart/index.tsx index 2be9c5a3..74edb402 100644 --- a/client/src/components/FlightQuickStart/index.tsx +++ b/client/src/components/FlightQuickStart/index.tsx @@ -2,7 +2,7 @@ import Button from "@thorium/ui/Button"; import Modal from "@thorium/ui/Modal"; import {netSend} from "client/src/context/netSend"; import {toast} from "client/src/context/ToastContext"; -import {useClientData} from "client/src/context/useCardData"; +import {useNetRequest} from "client/src/context/useNetRequest"; import {useMatch, useNavigate, Navigate, Outlet, Link} from "react-router-dom"; import {randomNameGenerator} from "server/src/utils/randomNameGenerator"; import {useFlightQuickStart} from "./FlightQuickStartContext"; @@ -11,7 +11,8 @@ function capitalize(val: string) { return val.charAt(0).toUpperCase() + val.slice(1); } export default function FlightQuickStart() { - const clientData = useClientData(); + const flight = useNetRequest("flight"); + const client = useNetRequest("client"); const [state, dispatch] = useFlightQuickStart(); @@ -20,8 +21,8 @@ export default function FlightQuickStart() { const match = useMatch("/flight/quick/:step"); if (!match) return ; - if (clientData.flight) return ; - if (!clientData.client.isHost) return ; + if (flight) return ; + if (!client.isHost) return ; const {step} = match.params; diff --git a/client/src/components/Station/CardArea.tsx b/client/src/components/Station/CardArea.tsx index be24859d..1b6c1d3d 100644 --- a/client/src/components/Station/CardArea.tsx +++ b/client/src/components/Station/CardArea.tsx @@ -5,9 +5,9 @@ import {ComponentType, Fragment, Suspense, useState} from "react"; import {ErrorBoundary} from "react-error-boundary"; import {Transition} from "@headlessui/react"; import {CardProps} from "./CardProps"; -import {useClientData} from "client/src/context/useCardData"; import {LoadingSpinner} from "@thorium/ui/LoadingSpinner"; import CardProvider from "client/src/context/CardContext"; +import {useNetRequest} from "client/src/context/useNetRequest"; const CardError = () => { return ( @@ -33,9 +33,10 @@ const transitionProps = { }; export const CardArea: React.FC<{ - card: ReturnType["station"]["cards"][0]; + card: {component: string}; }> = ({card}) => { - const {client, station} = useClientData(); + const client = useNetRequest("client"); + const station = useNetRequest("station"); const CardComponents = station.cards.map(card => ({ ...card, CardComponent: Cards[card.component as keyof typeof Cards], @@ -69,7 +70,7 @@ const CardRenderer = ({ id: string; currentCardId: string; }) => { - const {client} = useClientData(); + const client = useNetRequest("client"); const allowCard = Boolean(client.loginName) && !client.offlineState; const [cardLoaded, setCardLoaded] = useState(false); const show = allowCard && currentCardId === id; diff --git a/client/src/components/Station/CardSwitcher.tsx b/client/src/components/Station/CardSwitcher.tsx index 572543f1..53bc64a6 100644 --- a/client/src/components/Station/CardSwitcher.tsx +++ b/client/src/components/Station/CardSwitcher.tsx @@ -1,5 +1,5 @@ import {SVGImageLoader} from "@thorium/ui/SVGImageLoader"; -import {useClientData} from "client/src/context/useCardData"; +import {useNetRequest} from "client/src/context/useNetRequest"; const CardButton: React.FC<{ active: boolean; @@ -28,7 +28,7 @@ export const CardSwitcher: React.FC<{ card: string; changeCard: (id: string) => void; }> = ({card, changeCard}) => { - const {station} = useClientData(); + const station = useNetRequest("station"); return (
diff --git a/client/src/components/Station/StationLayout.tsx b/client/src/components/Station/StationLayout.tsx index 7ddc8173..8990cb81 100644 --- a/client/src/components/Station/StationLayout.tsx +++ b/client/src/components/Station/StationLayout.tsx @@ -1,7 +1,7 @@ import {SVGImageLoader} from "@thorium/ui/SVGImageLoader"; import {netSend} from "client/src/context/netSend"; import {useThoriumAccount} from "client/src/context/ThoriumAccountContext"; -import {useClientData} from "client/src/context/useCardData"; +import {useNetRequest} from "client/src/context/useNetRequest"; import {RiLogoutCircleRLine} from "react-icons/ri"; import {CardArea} from "./CardArea"; import {CardSwitcher} from "./CardSwitcher"; @@ -9,14 +9,17 @@ import {useManageCard} from "./useManageCard"; import {ClickWidget} from "./widgets"; const StationLayout = () => { - const {ship, client, station, theme} = useClientData(); + const ship = useNetRequest("ship"); + const client = useNetRequest("client"); + const station = useNetRequest("station"); + const theme = useNetRequest("theme"); const [card, changeCard] = useManageCard(); - if (!ship) throw new Promise(() => {}); + const {account} = useThoriumAccount(); + if (!ship) return null; // TODO November 29, 2021: Get the proper alert level and put it here. // @ts-expect-error See above const alertLevel = ship.alertLevel || "5"; - const {account} = useThoriumAccount(); return (
import("../FlightDirector")); const StationLayout = lazy(() => import("./StationLayout")); const Effects = lazy(() => import("./Effects")); const StationWrapper = () => { - const {client, station} = useClientData(); - + const client = useNetRequest("client"); + const station = useNetRequest("station"); // TODO November 29, 2021: Include sound player here // TODO November 29, 2021: Include some kind of alert toast notification thing here // The existing alerts won't be targeted by the theme, so we need to embed it here. diff --git a/client/src/components/Station/useManageCard.tsx b/client/src/components/Station/useManageCard.tsx index e658353c..4676f1ce 100644 --- a/client/src/components/Station/useManageCard.tsx +++ b/client/src/components/Station/useManageCard.tsx @@ -1,8 +1,8 @@ -import {useClientData} from "client/src/context/useCardData"; +import {useNetRequest} from "client/src/context/useNetRequest"; import {useCallback, useRef, useState} from "react"; export function useManageCard() { - const {station} = useClientData(); + const station = useNetRequest("station"); const [currentCard, setCurrentCard] = useState( station.cards[0]?.component || "" ); diff --git a/client/src/components/WelcomeButtons.tsx b/client/src/components/WelcomeButtons.tsx index 4f00be7d..2e26ce80 100644 --- a/client/src/components/WelcomeButtons.tsx +++ b/client/src/components/WelcomeButtons.tsx @@ -1,5 +1,4 @@ import {NavLink} from "react-router-dom"; -import {useClientData} from "../context/useCardData"; import Button from "@thorium/ui/Button"; import {Disclosure} from "@headlessui/react"; import {netSend} from "../context/netSend"; @@ -7,12 +6,13 @@ import {useNetRequest} from "../context/useNetRequest"; export const WelcomeButtons = ({className}: {className?: string}) => { const flights = useNetRequest("flights"); - const clientData = useClientData(); + const flight = useNetRequest("flight"); + const client = useNetRequest("client"); return (
- {clientData.flight ? ( + {flight ? ( <> Go To Flight Lobby @@ -31,7 +31,7 @@ export const WelcomeButtons = ({className}: {className?: string}) => { ) : ( <> - {clientData.client.isHost && ( + {client.isHost && ( <> { )} - {clientData.client.isHost && ( + {client.isHost && ( Configure Plugins diff --git a/client/src/components/WelcomeLogo.tsx b/client/src/components/WelcomeLogo.tsx index c8fbae34..4aca8f9a 100644 --- a/client/src/components/WelcomeLogo.tsx +++ b/client/src/components/WelcomeLogo.tsx @@ -3,10 +3,10 @@ import Logo from "../images/logo.svg?url"; import packageJson from "../../../package.json"; import {ClientButton} from "./ClientButton"; import {useEffect, useState} from "react"; -import {useClientData} from "../context/useCardData"; import Button from "@thorium/ui/Button"; import {netSend} from "../context/netSend"; import {CopyToClipboard} from "./ui/CopyToClipboard"; +import {useNetRequest} from "../context/useNetRequest"; function useConnectionAddress() { const [connectionAddress, setConnectionAddress] = useState(""); @@ -18,7 +18,7 @@ function useConnectionAddress() { } export const WelcomeLogo = ({className}: {className?: string}) => { const connectionAddress = useConnectionAddress(); - const clientData = useClientData(); + const thoriumData = useNetRequest("thorium"); const [updateText, setUpdateText] = useState(""); useEffect(() => { window.thorium?.registerUpdateHandler(message => { @@ -61,7 +61,7 @@ export const WelcomeLogo = ({className}: {className?: string}) => { )} - {clientData.thorium.hasHost ? null : ( + {thoriumData.hasHost ? null : (