AstroCol/src/pages/api/fleet/send.ts

268 lines
9.3 KiB
TypeScript

import { APIRoute } from "astro";
import validateAccessToken from "../../../lib/utils/validateAccessToken";
import { getUserByAccessToken } from "../../../lib/db/users";
import { ObjectId } from "mongodb";
import locationManager from "../../../lib/classes/managers/LocationManager";
import { getAllShips } from "../../../lib/db/ships";
import DBShip from "../../../types/db/DBShip";
import { Planet } from "../../../lib/classes/managers/PlanetManager";
import MissionType from "../../../types/MissionType";
import { getAllResources } from "../../../lib/db/resources";
import FleetManager from "../../../lib/classes/managers/FleetManager";
export const POST: APIRoute = async({ request }) => {
const response = await validateAccessToken(request);
if(response instanceof Response) return response;
const userDB = await getUserByAccessToken(response);
if(userDB === null) {
return new Response(
JSON.stringify({
code: 401,
message: "Unauthorized"
}), { status: 401 }
)
}
const user = locationManager.getUser(userDB._id);
if(!user) {
return new Response(
JSON.stringify({
code: 401,
message: "Unauthorized"
}), { status: 401 }
)
}
let body: { source: string, destination: string, ships: Array<{ id: string, amount: number }>, cargo: Array<{ id: string, amount: number }>, mission: MissionType };
try {
body = await request.json()
} catch(e) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid JSON body"
}), { status: 400 }
)
}
const checkSource = checkPlanetId(body.source, 'source');
if(typeof checkSource.error !== "undefined") return new Response(JSON.stringify(checkSource), { status: checkSource.code });
const checkDestination = checkPlanetId(body.destination, 'destination');
if(typeof checkDestination.error !== "undefined") return new Response(JSON.stringify(checkDestination), { status: checkDestination.code });
const source = checkSource.planet;
const destination = checkDestination.planet;
const shipsDB = await getAllShips();
const checkShipsBody = checkShips(body.ships, shipsDB, source);
if(typeof checkShipsBody.error !== "undefined") return new Response(JSON.stringify(checkShipsBody), { status: checkShipsBody.code });
const resourcesDB = await getAllResources();
await source.resources.calculateCurrentAvailableResources();
const checkCargoBody = checkCargo(body.cargo, body.ships, source, body.mission);
if(typeof checkCargoBody.error !== "undefined") return new Response(JSON.stringify(checkCargoBody), { status: checkCargoBody.code });
const fleetManager = new FleetManager(
new ObjectId(),
source,
destination,
new Date(),
new Date(Date.now() + 1000 * 30), //TODO: calculate time based on distance
false,
body.mission,
body.ships,
body.cargo
);
const resourceDiff = await source.resources.getDifference(body.cargo.map(c => ({ id: c.id, amount: c.amount })));
for(const res of resourceDiff) {
if(res.amount < 0) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Not enough resource with ID '" + res.id + "' on source planet"
}), { status: 400 }
)
}
}
source.resources.setAmount(resourceDiff);
await source.resources.sync();
for(const ship of body.ships) {
source.ships.removeShips(ship.id, ship.amount);
}
await source.ships.sync();
await fleetManager.sync();
locationManager.addFleet(fleetManager);
return new Response(
JSON.stringify({
code: 200,
message: "OK"
}), { status: 200 }
)
}
function checkPlanetId(id: string, type: string) {
if(typeof ObjectId === "undefined") return {
code: 400,
message: "Bad Request",
error: `Missing '${type}' in body`
}
let idToCheck;
try {
idToCheck = new ObjectId(id);
} catch(e) {
return {
code: 400,
message: "Bad Request",
error: `Invalid ID in '${type}'`
}
}
const planet = locationManager.getPlanet(idToCheck);
if(!planet) return {
code: 404,
message: "Not Found",
error: `Non-existent planet provided in '${type}'`
}
return {
code: 200,
message: "OK",
planet
}
}
function checkShips(ships: Array<{ id: string, amount: number }>, shipsDB: Array<DBShip>, sourcePlanet: Planet) {
if(typeof ships === "undefined") return {
code: 400,
message: "Bad Request",
error: "Missing 'ships' in body"
}
for(let i = 0; i < ships.length; i++) {
if(typeof ships[i].id === "undefined" || ships[i].id === "") return {
code: 400,
message: "Bad Request",
error: "Missing ship ID at position " + i
}
if(!shipsDB.find(ship => ship.id === ships[i].id)) return {
code: 404,
message: "Not Found",
error: "Non-existent ship ID '" + ships[i].id + "' at position " + i
}
if(typeof ships[i].amount === "undefined") return {
code: 400,
message: "Bad Request",
error: "Missing ship amount for ID '" + ships[i].id + "' at position " + i
}
if(ships[i].amount % 1 !== 0 || ships[i].amount < 0) return {
code: 400,
message: "Bad Request",
error: "Ship amount for ID '" + ships[i].id + "' is not a non-negative integer at position " + i
}
if(ships[i].amount > (sourcePlanet.ships.getShipById(ships[i].id)?.amount ?? 0)) return {
code: 400,
message: "Bad Request",
error: "Not enough ships on planet with ID '" + ships[i].id + "' at position " + i
}
}
return {
code: 200,
message: "OK"
}
}
function checkCargo(cargo: Array<{ id: string, amount: number }>, ships: Array<{ id: string, amount: number }>, planet: Planet, missionType: MissionType) {
if(missionType === "TRANSPORT" && cargo.length === 0) return {
code: 400,
message: "Bad Request",
error: "Missing 'cargo' in body for requested mission 'TRANSPORT'"
}
for(let i = 0; i < cargo.length; i++) {
if(typeof cargo[i].id === "undefined") return {
code: 400,
message: "Bad Request",
error: "Missing resource ID at position " + i
}
if(!planet.resources.resourcesDB.find(resource => resource.id === cargo[i].id)) return {
code: 404,
message: "Not Found",
error: "Non-existent resource ID '" + cargo[i].id + "' at position " + i
}
if(typeof cargo[i].amount === "undefined") return {
code: 400,
message: "Bad Request",
error: "Missing resource amount for ID '" + cargo[i].id + "' at position " + i
}
if(cargo[i].amount % 1 !== 0 || cargo[i].amount < 0) return {
code: 400,
message: "Bad Request",
error: "Resource amount for ID '" + cargo[i].id + "' is not a non-negative integer at position " + i
}
if(cargo[i].amount > (planet.resources.resources.find(res => res.id === cargo[i].id)?.amount ?? 0)) return {
code: 400,
message: "Bad Request",
error: "Not enough resources on planet with ID '" + cargo[i].id + "' at position " + i
}
}
const totalCargoAvailable = {
solid: ships.reduce((acc, ship) => acc + (planet.ships.shipsDB.find(s => s.id === ship.id)?.capacity.solid ?? 0) * ship.amount, 0),
liquid: ships.reduce((acc, ship) => acc + (planet.ships.shipsDB.find(s => s.id === ship.id)?.capacity.liquid ?? 0) * ship.amount, 0),
gas: ships.reduce((acc, ship) => acc + (planet.ships.shipsDB.find(s => s.id === ship.id)?.capacity.gas ?? 0) * ship.amount, 0)
}
const totalCargoUsed = {
solid: cargo.reduce((acc, resource) => planet.resources.resourcesDB.find(res => res.id === resource.id)?.type === "solid" ? acc + resource.amount : acc, 0),
liquid: cargo.reduce((acc, resource) => planet.resources.resourcesDB.find(res => res.id === resource.id)?.type === "liquid" ? acc + resource.amount : acc, 0),
gas: cargo.reduce((acc, resource) => planet.resources.resourcesDB.find(res => res.id === resource.id)?.type === "gas" ? acc + resource.amount : acc, 0)
}
if(totalCargoUsed.solid > totalCargoAvailable.solid) return {
code: 400,
message: "Bad Request",
error: "Not enough solid cargo capacity on ships"
}
if(totalCargoUsed.liquid > totalCargoAvailable.liquid) return {
code: 400,
message: "Bad Request",
error: "Not enough liquid cargo capacity on ships"
}
if(totalCargoUsed.gas > totalCargoAvailable.gas) return {
code: 400,
message: "Bad Request",
error: "Not enough gas cargo capacity on ships"
}
//TODO: check for fuel
return {
code: 200,
message: "OK"
}
}