From 44a9894fe77eb0f6c06da3d67d7c1cf17394cdec Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 23 Feb 2023 01:59:54 +0000 Subject: [PATCH 1/4] chore(release): 1.0.0-alpha.10 # [1.0.0-alpha.10](https://github.com/Thorium-Sim/thorium-nova/compare/1.0.0-alpha.9...1.0.0-alpha.10) (2023-02-23) ### Bug Fixes * Add back in the "Reconnect to Server" dialogs ([7f34e1c](https://github.com/Thorium-Sim/thorium-nova/commit/7f34e1cfce0619209b3496c29cdba2b59f6f7915)) * Auto-claim host when first connecting. ([52b4d75](https://github.com/Thorium-Sim/thorium-nova/commit/52b4d757daac0ebd940df30d30e6c1635a019828)) * Config UX improvements. ([e5bd455](https://github.com/Thorium-Sim/thorium-nova/commit/e5bd4554fe8134dcd2a49ee30aa32860c28ea870)) * Darken the background when configuring plugins. ([d20f426](https://github.com/Thorium-Sim/thorium-nova/commit/d20f426cfb9085ac6fef40d08dbab90dc189d6b7)) * Flight Director right-click to spawn and order ships. ([4cbcb0e](https://github.com/Thorium-Sim/thorium-nova/commit/4cbcb0e33d98ea8095a687e0f580d0125257da84)) * If a ship doesn't have a theme assigned to it, only automatically assign themes marked as default. ([004c954](https://github.com/Thorium-Sim/thorium-nova/commit/004c954cd17724f212bdbf0b15f632ea713b04c6)) * Interstellar viewscreen no longer shows the Flight Director view. ([e8c9840](https://github.com/Thorium-Sim/thorium-nova/commit/e8c98403190a7674b2d5e52767226af370353bb1)) * Navigation no longer crashes when transitioning to interstellar space. ([acab5f5](https://github.com/Thorium-Sim/thorium-nova/commit/acab5f5dcf66e142037ab9afdb08c01ef4f7e248)) * Properly save flights periodically while the app is open and when it closes. ([55ca019](https://github.com/Thorium-Sim/thorium-nova/commit/55ca0199ef2c666c6f5b687f0eb5c3036373f942)) * Refactor inventory to use liters instead of cubic meters. ([6d0e95f](https://github.com/Thorium-Sim/thorium-nova/commit/6d0e95f31f14c37b0313ea674018d45a90107849)) * Resolves backend errors when visiting plugin config page. ([be8fb36](https://github.com/Thorium-Sim/thorium-nova/commit/be8fb3696e50c58ff38c1f63b477b123c75ef3ef)) * Send initial data stream when a client first requests it. ([9a94fc2](https://github.com/Thorium-Sim/thorium-nova/commit/9a94fc2c6aff13cabe34fd7e09ce92e1a208377d)) * System specific config overrides ([1bb73da](https://github.com/Thorium-Sim/thorium-nova/commit/1bb73da04059652465d859d1f5a872bf86a0622b)) ### Features * Add a button to restore the default plugin, which will also update the default plugin contents with new Thorium Nova versions. ([82388a8](https://github.com/Thorium-Sim/thorium-nova/commit/82388a8e6ca5cd4d298d0a4e200748e1b9aa1c03)) * Enable snapping network data instead of smoothly interpolating. ([2ab84d8](https://github.com/Thorium-Sim/thorium-nova/commit/2ab84d89a6c9ac6363743d9e11ec16b005e0dd55)) * Improve the behavior of autopilot rotation. ([0ca5723](https://github.com/Thorium-Sim/thorium-nova/commit/0ca57238f4a3e61d824dec5d03a355e29d2520a1)) * Randomly cycle through backgrounds. ([945dd22](https://github.com/Thorium-Sim/thorium-nova/commit/945dd2240ecefb69eed894ff95c4d13d2d615a7e)) --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c2f71d6..166bea09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.0.0-alpha.10](https://github.com/Thorium-Sim/thorium-nova/compare/1.0.0-alpha.9...1.0.0-alpha.10) (2023-02-23) + + +### Bug Fixes + +* Add back in the "Reconnect to Server" dialogs ([7f34e1c](https://github.com/Thorium-Sim/thorium-nova/commit/7f34e1cfce0619209b3496c29cdba2b59f6f7915)) +* Auto-claim host when first connecting. ([52b4d75](https://github.com/Thorium-Sim/thorium-nova/commit/52b4d757daac0ebd940df30d30e6c1635a019828)) +* Config UX improvements. ([e5bd455](https://github.com/Thorium-Sim/thorium-nova/commit/e5bd4554fe8134dcd2a49ee30aa32860c28ea870)) +* Darken the background when configuring plugins. ([d20f426](https://github.com/Thorium-Sim/thorium-nova/commit/d20f426cfb9085ac6fef40d08dbab90dc189d6b7)) +* Flight Director right-click to spawn and order ships. ([4cbcb0e](https://github.com/Thorium-Sim/thorium-nova/commit/4cbcb0e33d98ea8095a687e0f580d0125257da84)) +* If a ship doesn't have a theme assigned to it, only automatically assign themes marked as default. ([004c954](https://github.com/Thorium-Sim/thorium-nova/commit/004c954cd17724f212bdbf0b15f632ea713b04c6)) +* Interstellar viewscreen no longer shows the Flight Director view. ([e8c9840](https://github.com/Thorium-Sim/thorium-nova/commit/e8c98403190a7674b2d5e52767226af370353bb1)) +* Navigation no longer crashes when transitioning to interstellar space. ([acab5f5](https://github.com/Thorium-Sim/thorium-nova/commit/acab5f5dcf66e142037ab9afdb08c01ef4f7e248)) +* Properly save flights periodically while the app is open and when it closes. ([55ca019](https://github.com/Thorium-Sim/thorium-nova/commit/55ca0199ef2c666c6f5b687f0eb5c3036373f942)) +* Refactor inventory to use liters instead of cubic meters. ([6d0e95f](https://github.com/Thorium-Sim/thorium-nova/commit/6d0e95f31f14c37b0313ea674018d45a90107849)) +* Resolves backend errors when visiting plugin config page. ([be8fb36](https://github.com/Thorium-Sim/thorium-nova/commit/be8fb3696e50c58ff38c1f63b477b123c75ef3ef)) +* Send initial data stream when a client first requests it. ([9a94fc2](https://github.com/Thorium-Sim/thorium-nova/commit/9a94fc2c6aff13cabe34fd7e09ce92e1a208377d)) +* System specific config overrides ([1bb73da](https://github.com/Thorium-Sim/thorium-nova/commit/1bb73da04059652465d859d1f5a872bf86a0622b)) + + +### Features + +* Add a button to restore the default plugin, which will also update the default plugin contents with new Thorium Nova versions. ([82388a8](https://github.com/Thorium-Sim/thorium-nova/commit/82388a8e6ca5cd4d298d0a4e200748e1b9aa1c03)) +* Enable snapping network data instead of smoothly interpolating. ([2ab84d8](https://github.com/Thorium-Sim/thorium-nova/commit/2ab84d89a6c9ac6363743d9e11ec16b005e0dd55)) +* Improve the behavior of autopilot rotation. ([0ca5723](https://github.com/Thorium-Sim/thorium-nova/commit/0ca57238f4a3e61d824dec5d03a355e29d2520a1)) +* Randomly cycle through backgrounds. ([945dd22](https://github.com/Thorium-Sim/thorium-nova/commit/945dd2240ecefb69eed894ff95c4d13d2d615a7e)) + # [1.0.0-alpha.9](https://github.com/Thorium-Sim/thorium-nova/compare/1.0.0-alpha.8...1.0.0-alpha.9) (2023-02-08) diff --git a/package.json b/package.json index 4cbf7a8e..00041933 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "thorium-nova", - "version": "1.0.0-alpha.9", + "version": "1.0.0-alpha.10", "description": "Spaceship Simulator Controls Platform", "keywords": [], "author": "Alex Anderson", From 51001bfb85bd1eacbc2882e5329bd331de1b2968 Mon Sep 17 00:00:00 2001 From: Alex Anderson Date: Tue, 28 Feb 2023 18:52:47 -0500 Subject: [PATCH 2/4] feat: Add a simulation for distributing power through the power grid. --- server/src/components/power.ts | 3 - .../components/shipSystems/powerGrid/index.ts | 1 - .../shipSystems/powerGrid/isBattery.ts | 10 + .../shipSystems/powerGrid/isPowerNode.ts | 2 +- .../shipSystems/powerGrid/isReactor.ts | 5 + .../shipSystems/powerGrid/powerConnection.ts | 14 - server/src/spawners/ship.ts | 13 +- server/src/systems/PowerGridSystem.ts | 307 +++++++++++++++ .../systems/__test__/PowerGridSystem.test.ts | 357 ++++++++++++++++++ server/src/systems/index.ts | 2 + 10 files changed, 694 insertions(+), 20 deletions(-) delete mode 100644 server/src/components/shipSystems/powerGrid/powerConnection.ts create mode 100644 server/src/systems/PowerGridSystem.ts create mode 100644 server/src/systems/__test__/PowerGridSystem.test.ts diff --git a/server/src/components/power.ts b/server/src/components/power.ts index 26c6f9d9..02fe0c56 100644 --- a/server/src/components/power.ts +++ b/server/src/components/power.ts @@ -16,9 +16,6 @@ export class PowerComponent extends Component { /** The current power provided to this system, calculated every frame */ currentPower: MegaWatt = 10; - /** How much power the system is currently drawing, calculated every frame */ - powerDraw: MegaWatt = 0; - /** How much power is currently being requested. Could be more than the maxSafePower */ requestedPower: MegaWatt = 10; } diff --git a/server/src/components/shipSystems/powerGrid/index.ts b/server/src/components/shipSystems/powerGrid/index.ts index 449fb13d..9051a8b4 100644 --- a/server/src/components/shipSystems/powerGrid/index.ts +++ b/server/src/components/shipSystems/powerGrid/index.ts @@ -1,4 +1,3 @@ export * from "./isBattery"; export * from "./isPowerNode"; export * from "./isReactor"; -export * from "./powerConnection"; diff --git a/server/src/components/shipSystems/powerGrid/isBattery.ts b/server/src/components/shipSystems/powerGrid/isBattery.ts index c8b34f2e..be962266 100644 --- a/server/src/components/shipSystems/powerGrid/isBattery.ts +++ b/server/src/components/shipSystems/powerGrid/isBattery.ts @@ -4,11 +4,21 @@ import {Component} from "../../utils"; export class IsBatteryComponent extends Component { static id = "isBattery" as const; + /** + * The power nodes that are associated with this battery + */ + connectedNodes: number[] = []; + /** * The amount of power this battery can hold. This provides * 23 minutes of sustained power. */ capacity: MegaWattHour = 46; + + /** + * How much power the battery is currently storing + */ + storage: MegaWattHour = 46; /** * How much energy the battery can use to charge. Typically * batteries charge faster than they discharge, while capacitors diff --git a/server/src/components/shipSystems/powerGrid/isPowerNode.ts b/server/src/components/shipSystems/powerGrid/isPowerNode.ts index bccb56fb..bdde571e 100644 --- a/server/src/components/shipSystems/powerGrid/isPowerNode.ts +++ b/server/src/components/shipSystems/powerGrid/isPowerNode.ts @@ -19,5 +19,5 @@ export class IsPowerNodeComponent extends Component { * - Least Need First (first fill up the systems with the smallest power requirement) * - Most Need First (first fill up the systems with the largest power requirement) */ - distributionMode: "evenly" | "leastFirst" | "mostFirst" = "leastFirst"; + distributionMode: "evenly" | "leastFirst" | "mostFirst" = "evenly"; } diff --git a/server/src/components/shipSystems/powerGrid/isReactor.ts b/server/src/components/shipSystems/powerGrid/isReactor.ts index 9ac72f05..82e8328c 100644 --- a/server/src/components/shipSystems/powerGrid/isReactor.ts +++ b/server/src/components/shipSystems/powerGrid/isReactor.ts @@ -4,6 +4,11 @@ import {Component} from "../../utils"; export class IsReactorComponent extends Component { static id = "isReactor" as const; + /** + * The power nodes and batteries that are associated with this reactor + */ + connectedEntities: number[] = []; + /** * This will be set when the ship is spawned * based on the total power required diff --git a/server/src/components/shipSystems/powerGrid/powerConnection.ts b/server/src/components/shipSystems/powerGrid/powerConnection.ts deleted file mode 100644 index 1bd6ff2e..00000000 --- a/server/src/components/shipSystems/powerGrid/powerConnection.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {Component} from "../../utils"; - -export class PowerConnectionComponent extends Component { - static id = "powerConnection" as const; - - /** - * Entities which provide power to this entity - */ - inputEntities: number[] = []; - /** - * Entities which this entity provides power to - */ - outputEntities: number[] = []; -} diff --git a/server/src/spawners/ship.ts b/server/src/spawners/ship.ts index 49fdc4fa..bdaa0825 100644 --- a/server/src/spawners/ship.ts +++ b/server/src/spawners/ship.ts @@ -10,6 +10,7 @@ import {spawnShipSystem} from "./shipSystem"; import ReactorPlugin from "@server/classes/Plugins/ShipSystems/Reactor"; import BaseShipSystemPlugin from "@server/classes/Plugins/ShipSystems/BaseSystem"; import {getInventoryTemplates} from "@server/utils/getInventoryTemplates"; +import {battery} from "@client/pages/Config/data/systems/battery"; const systemCache: Record = {}; function getSystem( @@ -84,7 +85,7 @@ export function spawnShip( node.addComponent("isPowerNode", { maxConnections: 3, connectedSystems: [], - distributionMode: "leastFirst", + distributionMode: "evenly", }); powerNodes[name] = {entity: node, count: 0}; systemEntities.push(node); @@ -102,6 +103,16 @@ export function spawnShip( // Reactors are special, so take care of them later. break; + case "battery": { + const entity = spawnShipSystem(systemPlugin, system.overrides); + if (entity.components.isBattery) { + entity.components.isBattery.storage = + entity.components.isBattery.capacity; + } + systemEntities.push(entity); + + break; + } default: { const entity = spawnShipSystem(systemPlugin, system.overrides); systemEntities.push(entity); diff --git a/server/src/systems/PowerGridSystem.ts b/server/src/systems/PowerGridSystem.ts new file mode 100644 index 00000000..c9f3da53 --- /dev/null +++ b/server/src/systems/PowerGridSystem.ts @@ -0,0 +1,307 @@ +import {Entity, System} from "../utils/ecs"; + +/** + * There's a lot to weight with how power is distributed. + * First, there's the double-connection between reactors and power nodes. + * A power node might be connected to multiple reactors, and each reactor + * could be connected to multiple power nodes. + * + * Also, there's the issue of batteries, which shouldn't be charged until + * all of the power nodes connected to a reactor have been supplied all + * the power they need, but also reactors attached to a battery should be + * weighted less than other reactors to provide more of an opportunity for + * the batteries to be charged, at the expense of having a different reactor + * not connected to the battery generate more power to fulfill the power + * node requirements. + * + * This algorithm should be efficient, which means as little looping as possible. + */ + +export class PowerGridSystem extends System { + test(entity: Entity) { + return !!entity.components.isShip; + } + update(entity: Entity, elapsed: number) { + const elapsedTimeHours = elapsed / 1000 / 60 / 60; + let poweredSystems: Entity[] = []; + let reactors: Entity[] = []; + let batteries: Entity[] = []; + let powerNodes: Entity[] = []; + + const systemIds = entity.components.shipSystems?.shipSystems.keys() || []; + for (let sysId of systemIds) { + const sys = this.ecs.getEntityById(sysId); + if (sys?.components.isReactor) reactors.push(sys); + else if (sys?.components.isBattery) batteries.push(sys); + else if (sys?.components.isPowerNode) powerNodes.push(sys); + else if (sys?.components.isShipSystem && sys.components.power) + poweredSystems.push(sys); + } + + // First, figure out how much power each power node is requesting + const nodeRequestedPower = new Map(); + for (let node of powerNodes) { + let nodePower = 0; + for (let systemId of node.components.isPowerNode?.connectedSystems || + []) { + const system = poweredSystems.find(s => s.id === systemId); + nodePower += system?.components.power?.requestedPower || 0; + } + nodeRequestedPower.set(node.id, nodePower); + } + + // Sort reactors based on whether they are connected to batteries, + // and how many power nodes they are connected to. + reactors.sort((a, b) => { + if (!a.components.isReactor) return -1; + if (!b.components.isReactor) return 1; + const aBatteries = a.components.isReactor?.connectedEntities.filter(id => + batteries.find(b => b.id === id) + ).length; + const bBatteries = b.components.isReactor?.connectedEntities.filter(id => + batteries.find(b => b.id === id) + ).length; + if (aBatteries > bBatteries) return -1; + if (bBatteries > aBatteries) return 1; + + return ( + a.components.isReactor?.connectedEntities.length - + b.components.isReactor?.connectedEntities.length + ); + }); + // Supply reactor power to the power nodes, + // but only up to their requested power level + const nodeSuppliedPower = new Map(); + const batterySuppliedPower = new Map(); + for (let reactor of reactors) { + if (!reactor.components.isReactor) continue; + if (reactor.components.isReactor.connectedEntities.length === 0) continue; + + // Convert the total power output to the instantaneous output by dividing it by one hour + let totalPower = reactor.components.isReactor.currentOutput; + + // Distribute power to power nodes first + const reactorNodes = reactor.components.isReactor.connectedEntities + .map(id => { + const powerNode = powerNodes.find(node => node.id === id); + if (!powerNode) return null; + return { + id: powerNode.id, + requestedPower: Math.max( + 0, + (nodeRequestedPower.get(id) || 0) - + (nodeSuppliedPower.get(id) || 0) + ), + }; + }) + .filter(Boolean) as {id: number; requestedPower: number}[]; + + reactorNodes.sort((a, b) => { + return a.requestedPower - b.requestedPower; + }); + while (totalPower > 0) { + let powerSplit = totalPower / reactorNodes.length; + + const leastNode = reactorNodes[0]; + + if (!leastNode) break; + + // The least node doesn't need it's allotment of power, so let's + // give it all that it's asking for and split the rest among + // the other nodes + if (leastNode.requestedPower < powerSplit) { + reactorNodes.forEach(node => { + const currentPower = nodeSuppliedPower.get(node.id) || 0; + totalPower -= leastNode.requestedPower; + nodeSuppliedPower.set( + node.id, + leastNode.requestedPower + currentPower + ); + }); + reactorNodes.shift(); + continue; + } + + // There isn't enough power for all the nodes + // to get all that they want from this reactor + // so we'll give them all it can give. + reactorNodes.forEach(node => { + const currentPower = nodeSuppliedPower.get(node.id) || 0; + nodeSuppliedPower.set(node.id, currentPower + powerSplit); + totalPower -= powerSplit; + }); + break; + } + + // Is there power left over? Charge up the batteries + const reactorBatteries = reactor.components.isReactor.connectedEntities + .map(id => { + const battery = batteries.find(node => node.id === id); + if (!battery?.components.isBattery) return null; + const chargeCapacity = + (battery.components.isBattery?.chargeRate || 0) - + (batterySuppliedPower.get(id) || 0); + return { + id: battery.id, + requestedPower: Math.max(0, chargeCapacity), + }; + }) + .filter(Boolean) as {id: number; requestedPower: number}[]; + + reactorBatteries.sort((a, b) => { + return a.requestedPower - b.requestedPower; + }); + + while (totalPower > 0) { + let powerSplit = totalPower / reactorBatteries.length; + + const leastBattery = reactorBatteries[0]; + + if (!leastBattery) break; + + // The least node doesn't need it's allotment of power, so let's + // give it all that it's asking for and split the rest among + // the other nodes + if (leastBattery.requestedPower < powerSplit) { + reactorBatteries.forEach(battery => { + const currentPower = batterySuppliedPower.get(battery.id) || 0; + + totalPower -= leastBattery.requestedPower; + batterySuppliedPower.set( + battery.id, + leastBattery.requestedPower + currentPower + ); + }); + reactorBatteries.shift(); + continue; + } + + // There isn't enough power for all the batteries + // to get all that they want from this reactor + // so we'll give them all it can give. + reactorBatteries.forEach(node => { + const currentPower = batterySuppliedPower.get(node.id) || 0; + batterySuppliedPower.set(node.id, currentPower + powerSplit); + }); + break; + } + } + + // Now apply the battery power levels + batterySuppliedPower.forEach((value, key) => { + const battery = batteries.find(node => node.id === key); + const capacity = battery?.components.isBattery?.capacity || 0; + const storage = battery?.components.isBattery?.storage || 0; + const limit = battery?.components.isBattery?.chargeRate || Infinity; + battery?.updateComponent("isBattery", { + storage: Math.min( + capacity, + storage + Math.min(value, limit) * elapsedTimeHours + ), + }); + }); + + // Distribute the power node power to all of the connected systems + nodeSuppliedPower.forEach((value, key) => { + const node = powerNodes.find(node => node.id === key); + if (value < (nodeRequestedPower.get(key) || 0)) { + // If a power node doesn't have sufficient power, + // draw that power from batteries + const connectedBatteries = batteries.filter(b => + b.components.isBattery?.connectedNodes.includes(key) + ); + let excessDemand = (nodeRequestedPower.get(key) || 0) - value; + connectedBatteries.forEach(battery => { + const limit = battery.components.isBattery?.dischargeRate || Infinity; + const storage = battery.components.isBattery?.storage || 0; + const powerDraw = Math.min(limit, excessDemand) * elapsedTimeHours; + if (storage > powerDraw) { + battery.updateComponent("isBattery", { + storage: Math.max(0, storage - powerDraw), + }); + excessDemand = 0; + value = nodeRequestedPower.get(key) || 0; + } else { + excessDemand -= storage / elapsedTimeHours; + value += storage / elapsedTimeHours; + battery.updateComponent("isBattery", {storage: 0}); + } + }); + } + // Distribute all of the power to systems based on the power node's distribution scheme + const connectedSystems = poweredSystems.filter(sys => + node?.components.isPowerNode?.connectedSystems.includes(sys.id) + ); + const distributionMode = + node?.components.isPowerNode?.distributionMode || "evenly"; + + connectedSystems.sort((a, b) => { + if (distributionMode === "mostFirst") { + return ( + (b.components.power?.requestedPower || 0) - + (a.components.power?.requestedPower || 0) + ); + } else { + return ( + (a.components.power?.requestedPower || 0) - + (b.components.power?.requestedPower || 0) + ); + } + }); + if (distributionMode === "evenly") { + connectedSystems.forEach(entity => { + entity.updateComponent("power", {currentPower: 0}); + }); + + while (value > 0) { + let powerSplit = value / connectedSystems.length; + const leastPowerRequired = connectedSystems[0]; + if (!leastPowerRequired) break; + + // The system with the least power need doesn't need it's allotment of power, so let's + // give it all that it's asking for and split the rest among the other systems + const requestedPower = + leastPowerRequired.components.power?.requestedPower || 0; + + if (requestedPower < powerSplit) { + connectedSystems.forEach(entity => { + value -= requestedPower; + const currentPower = entity.components.power?.currentPower || 0; + const sysRequestedPower = + entity.components.power?.requestedPower || 0; + entity.updateComponent("power", { + currentPower: Math.min( + sysRequestedPower, + requestedPower + currentPower + ), + }); + }); + connectedSystems.shift(); + continue; + } + // There isn't enough power for all the systems + // to get all that they want from this node + // so we'll give them all it can give. + connectedSystems.forEach(system => { + const currentPower = system.components.power?.currentPower || 0; + const requestedPower = system.components.power?.requestedPower || 0; + system.updateComponent("power", { + currentPower: Math.min(requestedPower, powerSplit + currentPower), + }); + }); + break; + } + } else { + connectedSystems.forEach(system => { + let powerDraw = Math.min( + system.components.power?.requestedPower || 0, + value + ); + if (powerDraw < 0) powerDraw = 0; + system.updateComponent("power", {currentPower: powerDraw}); + value -= powerDraw; + }); + } + }); + } +} diff --git a/server/src/systems/__test__/PowerGridSystem.test.ts b/server/src/systems/__test__/PowerGridSystem.test.ts new file mode 100644 index 00000000..2907ba94 --- /dev/null +++ b/server/src/systems/__test__/PowerGridSystem.test.ts @@ -0,0 +1,357 @@ +import {createMockDataContext} from "@server/utils/createMockDataContext"; +import {ECS, Entity} from "@server/utils/ecs"; +import {randomFromList} from "@server/utils/randomFromList"; +import {PowerGridSystem} from "../PowerGridSystem"; + +describe("PowerGridSystem", () => { + let ecs: ECS; + let ship: Entity; + beforeEach(() => { + const mockDataContext = createMockDataContext(); + + ecs = new ECS(mockDataContext.server); + ecs.addSystem(new PowerGridSystem()); + ship = new Entity(); + ship.addComponent("isShip"); + ship.addComponent("shipSystems"); + ecs.addEntity(ship); + }); + it("should work with a simple setup", () => { + const system = new Entity(); + system.addComponent("isShipSystem", {type: "generic"}); + system.addComponent("power", {requestedPower: 50, currentPower: 0}); + ship.components.shipSystems?.shipSystems.set(system.id, {}); + ecs.addEntity(system); + + const powerNode = new Entity(); + powerNode.addComponent("isPowerNode", { + maxConnections: 3, + connectedSystems: [system.id], + }); + ship.components.shipSystems?.shipSystems.set(powerNode.id, {}); + ecs.addEntity(powerNode); + + const reactor = new Entity(); + reactor.addComponent("isShipSystem", {type: "reactor"}); + reactor.addComponent("isReactor", { + currentOutput: 60, + connectedEntities: [powerNode.id], + }); + ship.components.shipSystems?.shipSystems.set(reactor.id, {}); + ecs.addEntity(reactor); + + expect(system.components.power?.currentPower).toEqual(0); + + ecs.update(16); + expect(system.components.power?.currentPower).toEqual(50); + + reactor.updateComponent("isReactor", {currentOutput: 10}); + ecs.update(16); + expect(system.components.power?.currentPower).toEqual(10); + + system.updateComponent("power", {requestedPower: 5}); + ecs.update(16); + expect(system.components.power?.currentPower).toEqual(5); + }); + + it("should properly distribute power from a single reactor to multiple systems", () => { + const system1 = new Entity(); + system1.addComponent("isShipSystem", {type: "generic"}); + system1.addComponent("power", {requestedPower: 50, currentPower: 0}); + ship.components.shipSystems?.shipSystems.set(system1.id, {}); + ecs.addEntity(system1); + const system2 = new Entity(); + system2.addComponent("isShipSystem", {type: "generic"}); + system2.addComponent("power", {requestedPower: 50, currentPower: 0}); + ship.components.shipSystems?.shipSystems.set(system2.id, {}); + ecs.addEntity(system2); + + const powerNode = new Entity(); + powerNode.addComponent("isPowerNode", { + maxConnections: 3, + connectedSystems: [system1.id, system2.id], + }); + ship.components.shipSystems?.shipSystems.set(powerNode.id, {}); + ecs.addEntity(powerNode); + + const reactor = new Entity(); + reactor.addComponent("isShipSystem", {type: "reactor"}); + reactor.addComponent("isReactor", { + currentOutput: 60, + connectedEntities: [powerNode.id], + }); + ship.components.shipSystems?.shipSystems.set(reactor.id, {}); + ecs.addEntity(reactor); + + ecs.update(16); + expect(system1.components.power?.currentPower).toEqual(30); + expect(system2.components.power?.currentPower).toEqual(30); + + system1.updateComponent("power", {requestedPower: 10}); + ecs.update(16); + expect(system1.components.power?.currentPower).toEqual(10); + expect(system2.components.power?.currentPower).toEqual(50); + + powerNode.updateComponent("isPowerNode", {distributionMode: "leastFirst"}); + reactor.updateComponent("isReactor", {currentOutput: 15}); + ecs.update(16); + expect(system1.components.power?.currentPower).toEqual(10); + expect(system2.components.power?.currentPower).toEqual(5); + + powerNode.updateComponent("isPowerNode", {distributionMode: "mostFirst"}); + ecs.update(16); + expect(system1.components.power?.currentPower).toEqual(0); + expect(system2.components.power?.currentPower).toEqual(15); + }); + it("should work with multiple reactors connected to multiple power nodes", () => { + const system1 = new Entity(); + system1.addComponent("isShipSystem", {type: "generic"}); + system1.addComponent("power", {requestedPower: 50, currentPower: 0}); + ship.components.shipSystems?.shipSystems.set(system1.id, {}); + ecs.addEntity(system1); + const system2 = new Entity(); + system2.addComponent("isShipSystem", {type: "generic"}); + system2.addComponent("power", {requestedPower: 50, currentPower: 0}); + ship.components.shipSystems?.shipSystems.set(system2.id, {}); + ecs.addEntity(system2); + const system3 = new Entity(); + system3.addComponent("isShipSystem", {type: "generic"}); + system3.addComponent("power", {requestedPower: 50, currentPower: 0}); + ship.components.shipSystems?.shipSystems.set(system3.id, {}); + ecs.addEntity(system3); + const system4 = new Entity(); + system4.addComponent("isShipSystem", {type: "generic"}); + system4.addComponent("power", {requestedPower: 50, currentPower: 0}); + ship.components.shipSystems?.shipSystems.set(system4.id, {}); + ecs.addEntity(system4); + + const powerNode1 = new Entity(); + powerNode1.addComponent("isPowerNode", { + maxConnections: 3, + connectedSystems: [system1.id, system2.id], + }); + ship.components.shipSystems?.shipSystems.set(powerNode1.id, {}); + ecs.addEntity(powerNode1); + const powerNode2 = new Entity(); + powerNode2.addComponent("isPowerNode", { + maxConnections: 3, + connectedSystems: [system3.id, system4.id], + }); + ship.components.shipSystems?.shipSystems.set(powerNode2.id, {}); + ecs.addEntity(powerNode2); + + const reactor = new Entity(); + reactor.addComponent("isShipSystem", {type: "reactor"}); + reactor.addComponent("isReactor", { + currentOutput: 60, + connectedEntities: [powerNode1.id, powerNode2.id], + }); + ship.components.shipSystems?.shipSystems.set(reactor.id, {}); + ecs.addEntity(reactor); + + ecs.update(16); + expect(system1.components.power?.currentPower).toEqual(15); + expect(system2.components.power?.currentPower).toEqual(15); + expect(system3.components.power?.currentPower).toEqual(15); + expect(system4.components.power?.currentPower).toEqual(15); + + const reactor2 = new Entity(); + reactor2.addComponent("isShipSystem", {type: "reactor"}); + reactor2.addComponent("isReactor", { + currentOutput: 60, + connectedEntities: [powerNode2.id], + }); + ship.components.shipSystems?.shipSystems.set(reactor2.id, {}); + ecs.addEntity(reactor2); + + ecs.update(16); + expect(system1.components.power?.currentPower).toEqual(15); + expect(system2.components.power?.currentPower).toEqual(15); + expect(system3.components.power?.currentPower).toEqual(45); + expect(system4.components.power?.currentPower).toEqual(45); + + system1.updateComponent("power", {requestedPower: 10}); + system2.updateComponent("power", {requestedPower: 10}); + + ecs.update(16); + expect(system1.components.power?.currentPower).toEqual(10); + expect(system2.components.power?.currentPower).toEqual(10); + expect(system3.components.power?.currentPower).toEqual(50); + expect(system4.components.power?.currentPower).toEqual(50); + + system1.updateComponent("power", {requestedPower: 50}); + system2.updateComponent("power", {requestedPower: 50}); + system3.updateComponent("power", {requestedPower: 10}); + system4.updateComponent("power", {requestedPower: 10}); + + ecs.update(16); + expect(system1.components.power?.currentPower).toEqual(30); + expect(system2.components.power?.currentPower).toEqual(30); + expect(system3.components.power?.currentPower).toEqual(10); + expect(system4.components.power?.currentPower).toEqual(10); + }); + it("should properly charge and discharge batteries", () => { + const system = new Entity(); + system.addComponent("isShipSystem", {type: "generic"}); + system.addComponent("power", {requestedPower: 50, currentPower: 0}); + ship.components.shipSystems?.shipSystems.set(system.id, {}); + ecs.addEntity(system); + + const powerNode = new Entity(); + powerNode.addComponent("isPowerNode", { + maxConnections: 3, + connectedSystems: [system.id], + }); + ship.components.shipSystems?.shipSystems.set(powerNode.id, {}); + ecs.addEntity(powerNode); + + const battery = new Entity(); + battery.addComponent("isShipSystem", {type: "battery"}); + battery.addComponent("isBattery", { + connectedNodes: [powerNode.id], + storage: 0, + }); + ship.components.shipSystems?.shipSystems.set(battery.id, {}); + ecs.addEntity(battery); + + const reactor = new Entity(); + reactor.addComponent("isShipSystem", {type: "reactor"}); + reactor.addComponent("isReactor", { + currentOutput: 30, + connectedEntities: [battery.id], + }); + ship.components.shipSystems?.shipSystems.set(reactor.id, {}); + ecs.addEntity(reactor); + + expect(battery.components.isBattery?.storage).toEqual(0); + ecs.update(16); + expect(battery.components.isBattery?.storage).toMatchInlineSnapshot( + `0.00013333333333333334` + ); + reactor.updateComponent("isReactor", {currentOutput: 180}); + battery.updateComponent("isBattery", {storage: 0}); + ecs.update(16); + const storage = battery.components.isBattery?.storage; + reactor.updateComponent("isReactor", {currentOutput: 500}); + battery.updateComponent("isBattery", {storage: 0}); + ecs.update(16); + expect(storage).toMatchInlineSnapshot(`0.0008000000000000001`); + expect(storage).toEqual(battery.components.isBattery?.storage); + + // It should take 16 minutes to fully charge a battery at this rate. + for (let i = 0; i < 60 * 60 * 16; i++) { + ecs.update(16); + } + expect(battery.components.isBattery?.storage).toMatchInlineSnapshot(`46`); + + reactor.updateComponent("isReactor", { + currentOutput: 50, + connectedEntities: [battery.id, powerNode.id], + }); + battery.updateComponent("isBattery", {storage: 10}); + for (let i = 0; i < 60; i++) { + ecs.update(16); + } + expect(battery.components.isBattery?.storage).toEqual(10); + reactor.updateComponent("isReactor", { + currentOutput: 30, + connectedEntities: [battery.id, powerNode.id], + }); + battery.updateComponent("isBattery", {storage: 10}); + ecs.update(16); + expect(battery.components.isBattery?.storage).toEqual(9.99991111111111); + for (let i = 0; i < 60; i++) { + ecs.update(16); + } + expect(battery.components.isBattery?.storage).toEqual(9.994577777777742); + }); + it("should perform decently well", () => { + const reactors = Array.from({length: 5}).map(() => { + const reactor = new Entity(); + reactor.addComponent("isShipSystem", {type: "reactor"}); + reactor.addComponent("isReactor", { + currentOutput: 60, + connectedEntities: [], + }); + ship.components.shipSystems?.shipSystems.set(reactor.id, {}); + ecs.addEntity(reactor); + return reactor; + }); + const powerNodes = Array.from({length: 5}).map(() => { + const powerNode = new Entity(); + powerNode.addComponent("isPowerNode", { + maxConnections: 3, + connectedSystems: [], + distributionMode: randomFromList([ + "evenly", + "leastNeed", + "mostNeed", + ]) as any, + }); + ship.components.shipSystems?.shipSystems.set(powerNode.id, {}); + ecs.addEntity(powerNode); + const nodeReactors = new Set(); + + nodeReactors.add(randomFromList(reactors)); + nodeReactors.add(randomFromList(reactors)); + nodeReactors.forEach(reactor => { + reactor.updateComponent("isReactor", { + connectedEntities: [ + ...(reactor.components.isReactor?.connectedEntities || []), + powerNode.id, + ], + }); + }); + return powerNode; + }); + + Array.from({length: 4}).map(() => { + const battery = new Entity(); + battery.addComponent("isShipSystem", {type: "battery"}); + const nodeSet = new Set(); + nodeSet.add(randomFromList(powerNodes)); + nodeSet.add(randomFromList(powerNodes)); + battery.addComponent("isBattery", { + connectedNodes: [...nodeSet.values()].map(n => n.id), + storage: 0, + }); + const reactorSet = new Set(); + reactorSet.add(randomFromList(reactors)); + reactorSet.add(randomFromList(reactors)); + reactorSet.forEach(reactor => { + reactor.updateComponent("isReactor", { + connectedEntities: [ + ...(reactor.components.isReactor?.connectedEntities || []), + battery.id, + ], + }); + }); + ship.components.shipSystems?.shipSystems.set(battery.id, {}); + ecs.addEntity(battery); + return battery; + }); + Array.from({length: 50}).map(() => { + const system = new Entity(); + system.addComponent("isShipSystem", {type: "generic"}); + system.addComponent("power", { + requestedPower: Math.random() * 100, + currentPower: 0, + }); + ship.components.shipSystems?.shipSystems.set(system.id, {}); + ecs.addEntity(system); + + const node = randomFromList(powerNodes); + node.updateComponent("isPowerNode", { + connectedSystems: [ + ...(node.components.isPowerNode?.connectedSystems || []), + system.id, + ], + }); + return system; + }); + + const time = performance.now(); + ecs.update(16); + expect(performance.now() - time).toBeLessThan(1); + }); +}); diff --git a/server/src/systems/index.ts b/server/src/systems/index.ts index 91ad274d..4e94da49 100644 --- a/server/src/systems/index.ts +++ b/server/src/systems/index.ts @@ -22,6 +22,7 @@ import {FilterInventorySystem} from "./FilterInventorySystem"; import {ReactorHeatSystem} from "./ReactorHeatSystem"; import {HeatToCoolantSystem} from "./HeatToCoolantSystem"; import {HeatDispersionSystem} from "./HeatDispersionSystem"; +import {PowerGridSystem} from "./PowerGridSystem"; const systems = [ FilterInventorySystem, @@ -32,6 +33,7 @@ const systems = [ TimerSystem, ReactorFuelSystem, ReactorHeatSystem, + PowerGridSystem, AutoRotateSystem, AutoThrustSystem, ThrusterSystem, From 5908434fdccc13e6495994b88132c602dbda5169 Mon Sep 17 00:00:00 2001 From: Alex Anderson Date: Tue, 28 Feb 2023 19:01:15 -0500 Subject: [PATCH 3/4] Draw power based on a systems usage, not necessarily on only what the system is requesting. --- server/src/components/power.ts | 9 ++++- server/src/systems/PowerGridSystem.ts | 21 ++++++------ .../systems/__test__/PowerGridSystem.test.ts | 34 +++++++++---------- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/server/src/components/power.ts b/server/src/components/power.ts index 02fe0c56..493d892c 100644 --- a/server/src/components/power.ts +++ b/server/src/components/power.ts @@ -13,9 +13,16 @@ export class PowerComponent extends Component { /** The threshold of power usage for safely using this system */ maxSafePower: MegaWatt = 20; - /** The current power provided to this system, calculated every frame */ + /** The current power provided to this system, calculated every frame. */ currentPower: MegaWatt = 10; + /** + * How much power the system is attempting to draw, calculated every frame. + * This will always be less than or equal to requested power. If the system + * isn't doing as much work, it won't draw as much power. + */ + powerDraw: MegaWatt = 0; + /** How much power is currently being requested. Could be more than the maxSafePower */ requestedPower: MegaWatt = 10; } diff --git a/server/src/systems/PowerGridSystem.ts b/server/src/systems/PowerGridSystem.ts index c9f3da53..de0d56ad 100644 --- a/server/src/systems/PowerGridSystem.ts +++ b/server/src/systems/PowerGridSystem.ts @@ -45,7 +45,7 @@ export class PowerGridSystem extends System { for (let systemId of node.components.isPowerNode?.connectedSystems || []) { const system = poweredSystems.find(s => s.id === systemId); - nodePower += system?.components.power?.requestedPower || 0; + nodePower += system?.components.power?.powerDraw || 0; } nodeRequestedPower.set(node.id, nodePower); } @@ -238,13 +238,13 @@ export class PowerGridSystem extends System { connectedSystems.sort((a, b) => { if (distributionMode === "mostFirst") { return ( - (b.components.power?.requestedPower || 0) - - (a.components.power?.requestedPower || 0) + (b.components.power?.powerDraw || 0) - + (a.components.power?.powerDraw || 0) ); } else { return ( - (a.components.power?.requestedPower || 0) - - (b.components.power?.requestedPower || 0) + (a.components.power?.powerDraw || 0) - + (b.components.power?.powerDraw || 0) ); } }); @@ -259,16 +259,15 @@ export class PowerGridSystem extends System { if (!leastPowerRequired) break; // The system with the least power need doesn't need it's allotment of power, so let's - // give it all that it's asking for and split the rest among the other systems + // give it all that it's trying to pull and split the rest among the other systems const requestedPower = - leastPowerRequired.components.power?.requestedPower || 0; + leastPowerRequired.components.power?.powerDraw || 0; if (requestedPower < powerSplit) { connectedSystems.forEach(entity => { value -= requestedPower; const currentPower = entity.components.power?.currentPower || 0; - const sysRequestedPower = - entity.components.power?.requestedPower || 0; + const sysRequestedPower = entity.components.power?.powerDraw || 0; entity.updateComponent("power", { currentPower: Math.min( sysRequestedPower, @@ -284,7 +283,7 @@ export class PowerGridSystem extends System { // so we'll give them all it can give. connectedSystems.forEach(system => { const currentPower = system.components.power?.currentPower || 0; - const requestedPower = system.components.power?.requestedPower || 0; + const requestedPower = system.components.power?.powerDraw || 0; system.updateComponent("power", { currentPower: Math.min(requestedPower, powerSplit + currentPower), }); @@ -294,7 +293,7 @@ export class PowerGridSystem extends System { } else { connectedSystems.forEach(system => { let powerDraw = Math.min( - system.components.power?.requestedPower || 0, + system.components.power?.powerDraw || 0, value ); if (powerDraw < 0) powerDraw = 0; diff --git a/server/src/systems/__test__/PowerGridSystem.test.ts b/server/src/systems/__test__/PowerGridSystem.test.ts index 2907ba94..3872f3f3 100644 --- a/server/src/systems/__test__/PowerGridSystem.test.ts +++ b/server/src/systems/__test__/PowerGridSystem.test.ts @@ -19,7 +19,7 @@ describe("PowerGridSystem", () => { it("should work with a simple setup", () => { const system = new Entity(); system.addComponent("isShipSystem", {type: "generic"}); - system.addComponent("power", {requestedPower: 50, currentPower: 0}); + system.addComponent("power", {powerDraw: 50, currentPower: 0}); ship.components.shipSystems?.shipSystems.set(system.id, {}); ecs.addEntity(system); @@ -49,7 +49,7 @@ describe("PowerGridSystem", () => { ecs.update(16); expect(system.components.power?.currentPower).toEqual(10); - system.updateComponent("power", {requestedPower: 5}); + system.updateComponent("power", {powerDraw: 5}); ecs.update(16); expect(system.components.power?.currentPower).toEqual(5); }); @@ -57,12 +57,12 @@ describe("PowerGridSystem", () => { it("should properly distribute power from a single reactor to multiple systems", () => { const system1 = new Entity(); system1.addComponent("isShipSystem", {type: "generic"}); - system1.addComponent("power", {requestedPower: 50, currentPower: 0}); + system1.addComponent("power", {powerDraw: 50, currentPower: 0}); ship.components.shipSystems?.shipSystems.set(system1.id, {}); ecs.addEntity(system1); const system2 = new Entity(); system2.addComponent("isShipSystem", {type: "generic"}); - system2.addComponent("power", {requestedPower: 50, currentPower: 0}); + system2.addComponent("power", {powerDraw: 50, currentPower: 0}); ship.components.shipSystems?.shipSystems.set(system2.id, {}); ecs.addEntity(system2); @@ -87,7 +87,7 @@ describe("PowerGridSystem", () => { expect(system1.components.power?.currentPower).toEqual(30); expect(system2.components.power?.currentPower).toEqual(30); - system1.updateComponent("power", {requestedPower: 10}); + system1.updateComponent("power", {powerDraw: 10}); ecs.update(16); expect(system1.components.power?.currentPower).toEqual(10); expect(system2.components.power?.currentPower).toEqual(50); @@ -106,22 +106,22 @@ describe("PowerGridSystem", () => { it("should work with multiple reactors connected to multiple power nodes", () => { const system1 = new Entity(); system1.addComponent("isShipSystem", {type: "generic"}); - system1.addComponent("power", {requestedPower: 50, currentPower: 0}); + system1.addComponent("power", {powerDraw: 50, currentPower: 0}); ship.components.shipSystems?.shipSystems.set(system1.id, {}); ecs.addEntity(system1); const system2 = new Entity(); system2.addComponent("isShipSystem", {type: "generic"}); - system2.addComponent("power", {requestedPower: 50, currentPower: 0}); + system2.addComponent("power", {powerDraw: 50, currentPower: 0}); ship.components.shipSystems?.shipSystems.set(system2.id, {}); ecs.addEntity(system2); const system3 = new Entity(); system3.addComponent("isShipSystem", {type: "generic"}); - system3.addComponent("power", {requestedPower: 50, currentPower: 0}); + system3.addComponent("power", {powerDraw: 50, currentPower: 0}); ship.components.shipSystems?.shipSystems.set(system3.id, {}); ecs.addEntity(system3); const system4 = new Entity(); system4.addComponent("isShipSystem", {type: "generic"}); - system4.addComponent("power", {requestedPower: 50, currentPower: 0}); + system4.addComponent("power", {powerDraw: 50, currentPower: 0}); ship.components.shipSystems?.shipSystems.set(system4.id, {}); ecs.addEntity(system4); @@ -170,8 +170,8 @@ describe("PowerGridSystem", () => { expect(system3.components.power?.currentPower).toEqual(45); expect(system4.components.power?.currentPower).toEqual(45); - system1.updateComponent("power", {requestedPower: 10}); - system2.updateComponent("power", {requestedPower: 10}); + system1.updateComponent("power", {powerDraw: 10}); + system2.updateComponent("power", {powerDraw: 10}); ecs.update(16); expect(system1.components.power?.currentPower).toEqual(10); @@ -179,10 +179,10 @@ describe("PowerGridSystem", () => { expect(system3.components.power?.currentPower).toEqual(50); expect(system4.components.power?.currentPower).toEqual(50); - system1.updateComponent("power", {requestedPower: 50}); - system2.updateComponent("power", {requestedPower: 50}); - system3.updateComponent("power", {requestedPower: 10}); - system4.updateComponent("power", {requestedPower: 10}); + system1.updateComponent("power", {powerDraw: 50}); + system2.updateComponent("power", {powerDraw: 50}); + system3.updateComponent("power", {powerDraw: 10}); + system4.updateComponent("power", {powerDraw: 10}); ecs.update(16); expect(system1.components.power?.currentPower).toEqual(30); @@ -193,7 +193,7 @@ describe("PowerGridSystem", () => { it("should properly charge and discharge batteries", () => { const system = new Entity(); system.addComponent("isShipSystem", {type: "generic"}); - system.addComponent("power", {requestedPower: 50, currentPower: 0}); + system.addComponent("power", {powerDraw: 50, currentPower: 0}); ship.components.shipSystems?.shipSystems.set(system.id, {}); ecs.addEntity(system); @@ -334,7 +334,7 @@ describe("PowerGridSystem", () => { const system = new Entity(); system.addComponent("isShipSystem", {type: "generic"}); system.addComponent("power", { - requestedPower: Math.random() * 100, + powerDraw: Math.random() * 100, currentPower: 0, }); ship.components.shipSystems?.shipSystems.set(system.id, {}); From b81ebe14162fff30e9cf1f7317cfbca4ad5d1915 Mon Sep 17 00:00:00 2001 From: Alex Anderson Date: Fri, 3 Mar 2023 19:24:37 -0500 Subject: [PATCH 4/4] Test fix --- server/src/systems/__test__/PowerGridSystem.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/systems/__test__/PowerGridSystem.test.ts b/server/src/systems/__test__/PowerGridSystem.test.ts index 3872f3f3..f6a92b55 100644 --- a/server/src/systems/__test__/PowerGridSystem.test.ts +++ b/server/src/systems/__test__/PowerGridSystem.test.ts @@ -352,6 +352,6 @@ describe("PowerGridSystem", () => { const time = performance.now(); ecs.update(16); - expect(performance.now() - time).toBeLessThan(1); + expect(performance.now() - time).toBeLessThan(3); }); });