Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Client): Make it possible to assign a client to a station. #118

Merged
merged 7 commits into from
Nov 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions client/src/cards/clients/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ import {DataContext} from "server/src/utils/DataContext";

export const subscriptions = {
clientList: async (context: DataContext) => {
const data = Object.values(context.server.clients);
return data;
const serverClients = Object.values(context.server.clients);
const flightClients = context.flight?.clients || {};
const clients = serverClients.map(client => {
const flightClient = flightClients[client.id];
return {
...client.toJSON(),
...flightClient?.toJSON(),
};
});
return clients;
},
};
2 changes: 1 addition & 1 deletion server/src/classes/FlightClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {BaseClient} from "./BaseClient";

export class FlightClient extends BaseClient {
flightId: string;
shipId: string | null;
shipId: number | null;
stationId: string | null;
loginName: string;
offlineState: OfflineStates | null;
Expand Down
2 changes: 1 addition & 1 deletion server/src/components/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export {PositionComponent} from "./position";
export {VelocityComponent} from "./velocity";
export {RotationComponent} from "./rotation";
export {ColorComponent} from "./color";
export {StationComplementComponent} from "./stationComplement";
export {RotationVelocityComponent} from "./rotationVelocity";
export {SizeComponent} from "./size";
export {TagsComponent} from "./tags";
export {StationComplementComponent} from "./stationComplement";
163 changes: 163 additions & 0 deletions server/src/inputs/__test__/client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import {ServerClient} from "server/src/classes/Client";
import type {ServerDataModel} from "server/src/classes/ServerDataModel";
import {FlightClient} from "server/src/classes/FlightClient";
import {ECS, Entity} from "server/src/utils/ecs";
import {clientInputs} from "../client";
import systems from "../../systems";
import type ShipPlugin from "server/src/classes/Plugins/Ship";
import {FlightDataModel} from "server/src/classes/FlightDataModel";

class MockServerDataModel {
clients!: Record<string, ServerClient>;
thoriumId!: string;
activeFlightName!: string | null;
plugins = [];
constructor() {
this.clients = {
test: new ServerClient({
id: "test",
}),
};
}
toJSON() {
const {plugins, ...data} = this;
return data;
}
}
class MockFlightDataModel {
static INTERVAL = 1000 / 60;
id: string = "Test Flight";
name: string = "Test Flight";
date: number = Date.now();
paused: boolean = false;
ecs!: ECS;
clients: Record<string, FlightClient> = {};
pluginIds: string[] = [];
private initEntities: Entity[] = [];
serverDataModel: ServerDataModel;
constructor(
params: Partial<MockFlightDataModel> & {
serverDataModel: ServerDataModel;
initialLoad?: boolean;
entities: Entity[];
}
) {
this.serverDataModel = params.serverDataModel;
this.initEntities = params.entities || [];
this.clients = {
test: new FlightClient({
id: "test",
flightId: this.id,
}),
};
}
run = () => {
// Run all the systems
if (!this.paused) {
this.ecs.update();
}
if (process.env.NODE_ENV === "test") return;
setTimeout(this.run, MockFlightDataModel.INTERVAL);
};
initEcs(server: ServerDataModel) {
this.ecs = new ECS(server);
systems.forEach(Sys => {
this.ecs.addSystem(new Sys());
});
this.initEntities.forEach(({id, components}) => {
const e = new Entity(id, components);
this.ecs.addEntity(e);
});
this.run();
}
get playerShips() {
return this.ecs.entities.filter(
f => f.components.isShip && f.components.isPlayerShip
);
}
get ships() {
return this.ecs.entities.filter(f => f.components.isShip);
}
get availableShips() {
const allShips = this.pluginIds.reduce((prev: ShipPlugin[], next) => {
const plugin = this.serverDataModel.plugins.find(
plugin => plugin.id === next
);
if (!plugin) return prev;
return prev.concat(plugin.aspects.ships);
}, []);
return allShips;
}
toJSON() {
// Get all of the entities in the world and serialize them into objects
return {
id: this.id,
name: this.name,
paused: this.paused,
date: this.date,
pluginIds: this.pluginIds,
entities: this.ecs.entities,
flightClients: Object.fromEntries(
Object.entries(this.clients).map(([id, client]) => [id, client])
),
};
}
}
class MockDataContext {
clientId: "test" = "test";
database: any = {};
server = new MockServerDataModel() as any as ServerDataModel;
flight = new MockFlightDataModel({
serverDataModel: this.server,
initialLoad: true,
entities: [],
}) as any as FlightDataModel;
constructor() {
this.flight.initEcs(this.server);
}
get client() {
return this.server.clients[this.clientId];
}
get flightClient() {
return this.findFlightClient(this.clientId);
}
findFlightClient(clientId: string) {
return this.flight.clients[clientId];
}
}
describe("Client input", () => {
it("should assign to a client and station", () => {
const mockDataContext = new MockDataContext();
expect(() =>
clientInputs.clientSetStation(mockDataContext, {
shipId: 1,
stationId: "Test",
})
).toThrowError("No ship with that ID exists");

const ship = mockDataContext.flight.ecs.addEntity(
new Entity(1, {
isShip: {shipClass: "Test", category: "Test", registry: "", assets: {}},
stationComplement: {
stations: [
{
name: "Test",
apiVersion: "stations/v1",
kind: "stations",
description: "",
logo: "",
theme: "",
tags: [],
cards: [],
},
],
},
})
);
clientInputs.clientSetStation(mockDataContext, {
shipId: 1,
stationId: "Test",
});
expect(mockDataContext.flight.clients.test.stationId).toBe("Test");
});
});
47 changes: 45 additions & 2 deletions server/src/inputs/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,50 @@ export const clientInputs = {
name: params.name,
};
},
clientDisconnect: (context: DataContext, params: {nothing: "hi"}) => {
return "goodbye" as const;
clientSetStation: (
context: DataContext,
params: {shipId: number | null; stationId: string; clientId?: string}
) => {
let flightClient = context.flightClient;
if (!flightClient) {
// TODO November 18, 2021 - Check to see if the client is the host of the flight.
// This will probably involve checking their Thorium account or associating their
// client ID with the flight somehow.
// For now, we'll just allow anyone to change anyone else's station.
let isHost = true;

if (!isHost || !params.clientId) {
throw new Error("No flight has been started.");
}
flightClient = context.findFlightClient(params.clientId);
if (!flightClient) {
throw new Error("No flight has been started.");
}
}

// If shipId is null, we're removing ourselves from the flight.
if (!params.shipId) {
flightClient.stationId = null;
flightClient.shipId = null;

return flightClient;
}
const ship = context.flight?.ships.find(ship => ship.id === params.shipId);
if (!ship) {
throw new Error("No ship with that ID exists.");
}
const station = ship.components.stationComplement?.stations.find(
station => station.name === params.stationId
);
if (!station) {
throw new Error("No station with that ID exists.");
}
flightClient.stationId = params.stationId;
flightClient.shipId = params.shipId;

pubsub.publish("clientList");
pubsub.publish("client", {clientId: context.clientId});

return flightClient;
},
};
11 changes: 7 additions & 4 deletions server/src/utils/DataContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ export class DataContext {
return this.database.server.clients[this.clientId];
}
get flightClient() {
return this.findFlightClient(this.clientId);
}
findFlightClient(clientId: string) {
if (!this.database.flight) return null;
if (!this.database.flight.clients[this.clientId]) {
this.database.flight.clients[this.clientId] = new FlightClient({
id: this.clientId,
if (!this.database.flight.clients[clientId]) {
this.database.flight.clients[clientId] = new FlightClient({
id: clientId,
flightId: this.database.flight.name,
});
}
return this.database.flight.clients[this.clientId];
return this.database.flight.clients[clientId];
}
}