diff --git a/src/components/ItemCard.astro b/src/components/ItemCard.astro index b0c10e6..8bc2019 100644 --- a/src/components/ItemCard.astro +++ b/src/components/ItemCard.astro @@ -3,6 +3,7 @@ import { getHighestWeightedLanguage, getLocales, getName } from '../lib/utils/la interface Props { id: string; + level: string; name: string; description: string; image: string; @@ -17,7 +18,7 @@ const lang = await getLocales(await getHighestWeightedLanguage(Astro.request.hea
-
{Astro.props.name}
+
{Astro.props.name} | {Astro.props.level}
{Astro.props.description}
{getName(lang, Astro.props.button_type, Astro.props.button_name)}
i
diff --git a/src/components/ResourceBar.astro b/src/components/ResourceBar.astro index 76cb44a..3033256 100644 --- a/src/components/ResourceBar.astro +++ b/src/components/ResourceBar.astro @@ -1,20 +1,26 @@ --- import { ObjectId } from 'mongodb'; -import { calculateCurrentAvailableResources } from '../lib/utils/resourceManager'; import { getHighestWeightedLanguage, getLocales, getName } from '../lib/utils/langDriver'; import { getAllResources } from '../lib/db/resources'; +import locationManager from '../lib/classes/managers/LocationManager'; +import { Resource } from '../lib/classes/managers/ResourceManager'; const resourceTypes = await getAllResources(); const lang = await getLocales(await getHighestWeightedLanguage(Astro.request.headers.get('accept-language'))); -const resources = await calculateCurrentAvailableResources(new ObjectId(Astro.cookies.get('planetid')?.value ?? '')); +const planetId = new ObjectId(Astro.cookies.get('planetid')?.value ?? ''); -const resourceArray = []; -for(const key in resources) { - resourceArray.push(resources[key as never]); +const resources = locationManager.getPlanet(planetId)?.resources; + +if(!resources) return; + +resources.calculateCurrentAvailableResources(); + +const resourceArray: Resource[] = []; +for(const key of resources.resources) { + resourceArray.push(key); } - ---
@@ -23,16 +29,16 @@ for(const key in resources) {
{resourceArray.map(res =>
x.id === res.name)?.type ?? "solid"} + data-res-type={resourceTypes.find(x => x.id === res.id)?.type ?? "solid"} data-res-amount={res.amount} data-res-mining-rate={res.perHourMiningRate} - style={(resourceTypes.find(x => x.id === res.name)?.type ?? "solid") === "solid" ? "" : "display: none;"} + style={(resourceTypes.find(x => x.id === res.id)?.type ?? "solid") === "solid" ? "" : "display: none;"} >
- x.id === res.name)?.icon ?? "#"} alt={res.name} /> + x.id === res.id)?.icon ?? "#"} alt={res.id} />
-
-
{getName(lang, 'resources', res.name)}
+
+
{getName(lang, 'resources', res.id)}
[fetching]
diff --git a/src/lib/classes/Building.ts b/src/lib/classes/Building.ts new file mode 100644 index 0000000..122158f --- /dev/null +++ b/src/lib/classes/Building.ts @@ -0,0 +1,70 @@ +import DBBuilding from "../../types/db/DBBuilding"; +import BuildingManager from "./managers/BuildingManager"; + +export default class Building { + manager: BuildingManager + data: DBBuilding; + level: number; + + constructor(manager: BuildingManager, data: DBBuilding, level: number) { + this.manager = manager; + this.data = data; + this.level = level; + } + + async checkRequiredResources(level: number): Promise { + const resources = await this.manager.planet.resources.calculateCurrentAvailableResources(); + + const requirements = this.data.requirements.resources; + + let canBuild = true; + + for(let res of requirements) { + const resource = resources.find(r => r.id === res.id); + if(!resource) return false; + + const required = Math.pow(this.data.multiplier, level) * res.amount; + + if(resource.amount < required) { + canBuild = false; + break; + } + } + + return canBuild; + } + + async checkRequirements(): Promise<{ canBuild: boolean, error: string }> { + const playerBuildings = this.manager.buildings; + let playerBuildingsCanBuild = { canBuild: true, missing: "" }; + this.data.requirements.buildings.forEach((buildingReq: any) => { + if(playerBuildings.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0 < buildingReq.level) { + playerBuildingsCanBuild = { canBuild: false, missing: `${buildingReq.id} level ${buildingReq.level} required, found ${playerBuildings.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0}` }; + return; + } + }); + if(!playerBuildingsCanBuild.canBuild) return { + canBuild: false, + error: playerBuildingsCanBuild.missing + } + + // research + const playerResearch = this.manager.planet.manager.owner.research; + let playerResearchCanBuild = { canBuild: true, missing: "" }; + for(const researchReq of this.data.requirements.research) { + if(playerResearch.research.find((research) => research.id === researchReq.id)?.level ?? 0 < researchReq.level) { + playerResearchCanBuild = { canBuild: false, missing: `${researchReq.id} level ${researchReq.level} required, found ${playerResearch.research.find((research) => research.id === researchReq.id)?.level ?? 0}` }; + } + }; + + if(!playerResearchCanBuild.canBuild) return { + canBuild: false, + error: playerResearchCanBuild.missing + } + + return { + canBuild: true, + error: "" + } + } +} \ No newline at end of file diff --git a/src/lib/classes/User.ts b/src/lib/classes/User.ts new file mode 100644 index 0000000..1b58122 --- /dev/null +++ b/src/lib/classes/User.ts @@ -0,0 +1,32 @@ +import { ObjectId } from "mongodb"; +import ResearchManager from "./managers/ResearchManager"; +import { Planet } from "./managers/PlanetManager"; + +export default class User { + id: ObjectId; + username: string; + email: string; + createdAt: Date; + updatedAt: Date; + lastLogin: Date; + research: ResearchManager; + mainPlanet!: Planet; + + constructor(id: ObjectId, username: string, email: string, createdAt: Date, updatedAt: Date, lastLogin: Date) { + this.id = id; + this.username = username; + this.email = email; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + this.lastLogin = lastLogin; + this.research = new ResearchManager(this); + } + + async init() { + await this.research.init(); + } + + addMainPlanet(planet: Planet) { + this.mainPlanet = planet; + } +} \ No newline at end of file diff --git a/src/lib/classes/managers/BuildingManager.ts b/src/lib/classes/managers/BuildingManager.ts new file mode 100644 index 0000000..8bca296 --- /dev/null +++ b/src/lib/classes/managers/BuildingManager.ts @@ -0,0 +1,48 @@ +import { getAllBuildings } from '../../db/buildings'; +import DBBuilding from '../../../types/db/DBBuilding'; +import { Planet } from './PlanetManager'; +import { updatePlanetBuildings } from '../../db/planets'; +import Building from '../Building'; + +export default class BuildingManager { + buildings: Array = []; + buildingsDB: Array = [] + planet: Planet; + + constructor(planet: Planet) { + this.planet = planet; + } + + async init(buildingData: { id: string, level: number }[]) { + this.buildingsDB = await getAllBuildings(); + buildingData.forEach(building => { + const buildingToFind = this.buildingsDB.find(b => b.id === building.id); + + if(buildingToFind) this.buildings.push(new Building( + this, + buildingToFind, + building.level + )) + }) + + return this; + } + + getBuildingById(id: string) { + return this.buildings.find(building => building.data.id === id); + } + + addBuilding(building: Building) { + const findBuilding = this.buildings.find(b => b.data.id === building.data.id); + if(!findBuilding) this.buildings.push(building); + else findBuilding.level++; + } + + removeBuilding(id: string) { + this.buildings = this.buildings.filter(building => building.data.id !== id); + } + + async sync() { + await updatePlanetBuildings(this.planet._id, this.buildings.map(building => { return { id: building.data.id, level: building.level } })); + } +} \ No newline at end of file diff --git a/src/lib/classes/managers/LocationManager.ts b/src/lib/classes/managers/LocationManager.ts new file mode 100644 index 0000000..a85a3fd --- /dev/null +++ b/src/lib/classes/managers/LocationManager.ts @@ -0,0 +1,153 @@ +import { ObjectId } from "mongodb"; +import PlanetManager, { Planet } from "./PlanetManager"; +import { getAllGalaxies } from "../../db/galaxies"; +import { getSectorById } from "../../db/sectors"; +import { getSystemById } from "../../db/systems"; +import { getAllUsers } from "../../db/users"; +import User from "../User"; + +export type Galaxy = { + _id: ObjectId, + name: string, + sectors: Array +} + +export type Sector = { + _id: ObjectId, + galaxy: Galaxy, + name: string, + expedition: null, + systems: Array +} + +export type System = { + _id: ObjectId, + sector: Sector, + name: string, + ownedBy: User, + planets: PlanetManager +} + +class LocationManager { + private static instance: LocationManager; + + private constructor() {} + + public static getInstance(): LocationManager { + if (!LocationManager.instance) { + LocationManager.instance = new LocationManager(); + } + return LocationManager.instance; + } + + galaxies: Galaxy[] = []; + users: User[] = []; + + async init() { + const users = await getAllUsers(); + users.forEach(async user => { + this.users.push(new User(user._id, user.username, user.email, user.createdAt, user.updatedAt, user.lastLogin)); + }); + + const galaxies = await getAllGalaxies(); + for(const galaxy of galaxies) { + const galaxyObject: Galaxy = { + _id: galaxy._id, + name: galaxy.name, + sectors: [] + }; + + for(const sectorId of galaxy.sectors) { + const sectorData = await getSectorById(sectorId); + + const sectorObject: Sector = { + _id: sectorData._id, + galaxy: galaxyObject, + name: sectorData.name, + expedition: null, + systems: [] + }; + + for(const systemId of sectorData.systems) { + const systemData = await getSystemById(systemId); + const user = this.users.find(user => user.id.equals(systemData.ownedBy)); + + if(!user) throw new Error("User not found"); + + const systemObject: System = { + _id: systemData._id, + sector: sectorObject, + name: systemData.name, + ownedBy: user, + planets: new PlanetManager(user) + }; + + await systemObject.planets.fillData(systemData.planets); + + sectorObject.systems.push(systemObject); + }; + + galaxyObject.sectors.push(sectorObject); + }; + + this.galaxies.push(galaxyObject); + }; + + for(const user of this.users) { + await user.init(); + const userDB = users.find(u => u._id.equals(user.id)); + const mainPlanet = this.getPlanet(userDB?.mainPlanet as ObjectId); + + if(!mainPlanet) throw new Error("Main planet not found"); + + user.mainPlanet = mainPlanet; + }; + + return this; + } + + getGalaxy(_id: ObjectId) { + return this.galaxies.find(galaxy => galaxy._id.equals(_id)); + } + + getSector(_id: ObjectId) { + let foundSector: Sector | undefined; + this.galaxies.find(galaxy => galaxy.sectors.find(sector => { + if(sector._id.equals(_id)) foundSector = sector; + })); + return foundSector; + } + + getSystem(_id: ObjectId) { + let foundSystem: System | undefined; + this.galaxies.find(galaxy => galaxy.sectors.find(sector => sector.systems.find(system => { + if(system._id.equals(_id)) foundSystem = system + }))); + return foundSystem + } + + getPlanet(_id: ObjectId) { + let foundPlanet: Planet | undefined; + + for(const galaxy of this.galaxies) { + for(const sector of galaxy.sectors) { + for(const system of sector.systems) { + foundPlanet = system.planets.getPlanetById(_id); + if(foundPlanet) break; + } + if(foundPlanet) break; + } + if(foundPlanet) break; + } + + return foundPlanet; + } + + getUser(_id: ObjectId) { + return this.users.find(user => user.id.equals(_id)); + } +} + +const locationManager = LocationManager.getInstance(); +locationManager.init(); +export default locationManager; \ No newline at end of file diff --git a/src/lib/classes/managers/PlanetManager.ts b/src/lib/classes/managers/PlanetManager.ts new file mode 100644 index 0000000..e5e22ff --- /dev/null +++ b/src/lib/classes/managers/PlanetManager.ts @@ -0,0 +1,51 @@ +import { ObjectId } from "mongodb"; +import BuildingManager from "./BuildingManager"; +import { getPlanetById } from "../../db/planets"; +import ResourceManager from "./ResourceManager"; +import User from "../User"; + +export type Planet = { + _id: ObjectId; + manager: PlanetManager; + name: string; + fields: number; + resources: ResourceManager; + buildings: BuildingManager; + ships: Array; +} + +export default class PlanetManager { + planets: Array = []; + owner: User; + + constructor(user: User) { + this.owner = user; + } + + async fillData(planets: ObjectId[]) { + await Promise.all(planets.map(async planetId => { + const planet = await getPlanetById(planetId); + + const planetObject: Planet = { + _id: planet._id, + manager: this, + name: planet.name, + fields: planet.fields, + //@ts-ignore + resources: null, + //@ts-ignore + buildings: null, + ships: planet.ships + } + + planetObject.resources = await new ResourceManager(planetObject).init(planet.resources); + planetObject.buildings = await new BuildingManager(planetObject).init(planet.buildings); + + this.planets.push(planetObject); + })); + } + + getPlanetById(id: ObjectId) { + return this.planets.find(planet => planet._id.equals(id)); + } +} \ No newline at end of file diff --git a/src/lib/classes/managers/ResearchManager.ts b/src/lib/classes/managers/ResearchManager.ts new file mode 100644 index 0000000..0bb9ab3 --- /dev/null +++ b/src/lib/classes/managers/ResearchManager.ts @@ -0,0 +1,50 @@ +import DBResearch from "../../../types/db/DBResearch"; +import { getAllResearch } from "../../db/research"; +import { getUserResearch, updateUserResearch } from "../../db/users"; +import User from "../User"; + +export type Research = { + id: string, + level: number, + data: DBResearch +} + +export default class ResearchManager { + research: Array = []; + researchDB: Array = []; + owner: User; + + constructor(user: User) { + this.owner = user; + } + + async init() { + this.researchDB = await getAllResearch(); + const research = await getUserResearch(this.owner); + this.research = research.map(r => { + const researchData = this.researchDB.find(research => research.id === r.id); + if(!researchData) throw new Error(`Research ${r.id} not found`); + return { + id: r.id, + level: r.level, + data: researchData + } + }); + + return this; + } + + getResearchById(id: string) { + return this.research.find(research => research.data.id === id); + } + + addResearch(research: Research) { + const findResearch = this.research.find(r => r.data.id === research.data.id); + if(!findResearch) this.research.push(research); + else findResearch.level++; + } + + async sync() { + await updateUserResearch(this.owner, this.research.map(r => { return { id: r.data.id, level: r.level } })); + } +} \ No newline at end of file diff --git a/src/lib/classes/managers/ResourceManager.ts b/src/lib/classes/managers/ResourceManager.ts new file mode 100644 index 0000000..476b832 --- /dev/null +++ b/src/lib/classes/managers/ResourceManager.ts @@ -0,0 +1,152 @@ +import DBResource from "../../../types/db/DBResource"; +import { updatePlanetResources } from "../../db/planets"; +import { getAllResources } from "../../db/resources"; +import { Planet } from "./PlanetManager"; + +export type Resource = { + id: string, + amount: number, + lastUpdated: Date, + perHourMiningRate: number, + data: DBResource +} + +export default class ResourceManager { + resources: Array = []; + planet: Planet; + + constructor(planet: Planet) { + this.planet = planet; + } + + async init(resourceData: { id: string, amount: number, lastUpdated: Date, perHourMiningRate: number }[]) { + const resources = await getAllResources(); + + if(resourceData.length === 0) { + resourceData = [ + { + id: "coal", + amount: 11, + lastUpdated: new Date(), + perHourMiningRate: 11 + }, + { + id: "iron", + amount: 22, + lastUpdated: new Date(), + perHourMiningRate: 22 + }, + { + id: "gold", + amount: 33, + lastUpdated: new Date(), + perHourMiningRate: 33 + }, + { + id: "water", + amount: 44, + lastUpdated: new Date(), + perHourMiningRate: 44 + }, + { + id: "sulfuric-acid", + amount: 55, + lastUpdated: new Date(), + perHourMiningRate: 55 + }, + { + id: "liquid-nitrogen", + amount: 66, + lastUpdated: new Date(), + perHourMiningRate: 66 + }, + { + id: "hydrogen", + amount: 77, + lastUpdated: new Date(), + perHourMiningRate: 77 + }, + { + id: "oxygen", + amount: 88, + lastUpdated: new Date(), + perHourMiningRate: 88 + }, + { + id: "helium-3", + amount: 99, + lastUpdated: new Date(), + perHourMiningRate: 99 + } + ]; + + await updatePlanetResources(this.planet._id, this.resources); + } + + resourceData.forEach(resource => { + const resFromDB = resources.find(res => res.id === resource.id); + if(resFromDB) this.resources.push({ + id: resource.id, + amount: resource.amount, + lastUpdated: resource.lastUpdated, + perHourMiningRate: resource.perHourMiningRate, + data: resFromDB + }) + }); + + return this; + } + + getResourceById(resId: string) { + return this.resources.find(res => res.id === resId); + } + + async calculateCurrentAvailableResources() { + this.resources.forEach(res => { + const timeDiff = Math.abs((new Date()).getTime() - res.lastUpdated.getTime()); + const hours = timeDiff / (1000 * 60 * 60); + const amountToAdd = hours * res.perHourMiningRate; + res.amount += amountToAdd; + res.lastUpdated = new Date(); + }); + + + await updatePlanetResources(this.planet._id, this.resources); + + return this.resources; + } + + async getDifference(resources: { id: string, amount: number }[]): Promise> { + const currentResources = await this.calculateCurrentAvailableResources(); + const difference: Resource[] = []; + + currentResources.forEach(res => { + const currentRes = resources.find(r => r.id === res.id); + if(currentRes) difference.push({ + id: res.id, + amount: res.amount - currentRes.amount, + lastUpdated: res.lastUpdated, + perHourMiningRate: res.perHourMiningRate, + data: res.data + }); + else difference.push(res); + }); + + return difference; + } + + update(resources: Resource[]) { + this.resources = resources; + } + + async sync() { + await updatePlanetResources(this.planet._id, this.resources.map(res => { + return { + id: res.id, + amount: res.amount, + lastUpdated: res.lastUpdated, + perHourMiningRate: res.perHourMiningRate + } + })); + } +} \ No newline at end of file diff --git a/src/lib/db/accessTokens.ts b/src/lib/db/accessTokens.ts index 78b6bbb..df99ce6 100644 --- a/src/lib/db/accessTokens.ts +++ b/src/lib/db/accessTokens.ts @@ -1,7 +1,9 @@ import { AccessTokens } from './mongodb'; -import type AccessToken from '../../types/AccessToken'; import { createHash } from 'crypto'; import { ObjectId } from 'mongodb'; +import DBAccessToken from '../../types/db/DBAccessToken'; +import AccessToken from '../../types/AccessToken'; +import locationManager from '../classes/managers/LocationManager'; export const createAccessToken = async (accessToken: AccessToken) => { const newAccessToken = await (await AccessTokens()).insertOne(accessToken); @@ -23,7 +25,7 @@ export const getAccessToken = async (accessToken: string) => { { createdAt }, { user }, { entropy } - ] }) as Promise; + ] }) as Promise; } export const getAllAccessTokens = async () => { @@ -37,7 +39,24 @@ export const getAllAccessTokens = async () => { } const accessTokens = await AccessTokens(); - const arrayOfTokens = await accessTokens.find({}).toArray() as AccessToken[]; - let arr = [master].concat(arrayOfTokens); + const arrayOfTokens = await accessTokens.find({}).toArray() as DBAccessToken[]; + + const arr = [master]; + + for(const token of arrayOfTokens) { + const user = locationManager.getUser(token.user) ?? null; + + const tokenObject: AccessToken = { + type: token.type, + user, + entropy: token.entropy, + createdAt: token.createdAt, + expiresAt: token.expiresAt, + createdFrom: token.createdFrom + } + + arr.push(tokenObject); + } + return arr; } \ No newline at end of file diff --git a/src/lib/db/buildings.ts b/src/lib/db/buildings.ts index 94a4546..c96231d 100644 --- a/src/lib/db/buildings.ts +++ b/src/lib/db/buildings.ts @@ -1,4 +1,4 @@ -import DBBuilding from '../../types/DBBuilding'; +import DBBuilding from '../../types/db/DBBuilding'; import { Buildings } from '../db/mongodb'; export const getAllBuildings = async () => { diff --git a/src/lib/db/galaxies.ts b/src/lib/db/galaxies.ts new file mode 100644 index 0000000..27cf0e9 --- /dev/null +++ b/src/lib/db/galaxies.ts @@ -0,0 +1,13 @@ +import { ObjectId } from "mongodb"; +import { Galaxies } from "./mongodb" +import DBGalaxy from "../../types/db/DBGalaxy"; + +export const getAllGalaxies = async () => { + return await (await Galaxies()).find({}).toArray() as DBGalaxy[]; +} + +export const getGalaxyById = async (id: ObjectId) => { + return await (await Galaxies()).findOne({ + _id: id + }) as DBGalaxy; +} \ No newline at end of file diff --git a/src/lib/db/mongodb.ts b/src/lib/db/mongodb.ts index 6d97a5e..153ef43 100644 --- a/src/lib/db/mongodb.ts +++ b/src/lib/db/mongodb.ts @@ -8,7 +8,6 @@ const mongo = new MongoClient(uri, options); export const connect = async () => { await mongo.connect(); - // return mongo.db(dbName); } export const disconnect = async () => { @@ -30,6 +29,21 @@ export const AccessTokens = async () => { return db.collection('accessTokens'); } +export const Galaxies = async () => { + const db = await getDB(); + return db.collection('galaxies'); +} + +export const Sectors = async () => { + const db = await getDB(); + return db.collection('sectors'); +} + +export const Systems = async () => { + const db = await getDB(); + return db.collection('systems'); +} + export const Planets = async () => { const db = await getDB(); return db.collection('planets'); diff --git a/src/lib/db/planets.ts b/src/lib/db/planets.ts index 1a8b341..798bee0 100644 --- a/src/lib/db/planets.ts +++ b/src/lib/db/planets.ts @@ -1,74 +1,29 @@ import { Planets } from '../db/mongodb'; -import type User from '../../types/User'; import { ObjectId } from 'mongodb'; -import type Planet from '../../types/Planet'; -import { getUserById } from './users'; -import type Building from '../../types/Building'; -import type Ship from '../../types/Ship'; -import type PlayerResource from '../../types/PlayerResource'; +import DBPlanet from '../../types/db/DBPlanet'; -export const getAllPlanets = async (options?: { fetchUserPlanets: boolean }) => { - const planets = await Planets(); - const fetched = (await planets.find({}).toArray()).map(async planet => { - planet.owner = await getUserById(planet.owner); - return planet; - }) - - return Promise.all(fetched) as unknown as Array; -} - -export const createPlanet = async (options: { name: string, owner?: User, ownerId?: ObjectId, fields?: number }) => { - if(options.owner !== undefined && options.ownerId !== undefined) throw new Error("Duplicate identifier"); - - let user: ObjectId; - if(options.ownerId) user = options.ownerId; - else if(options.owner) user = options.owner._id; - else throw new Error("Unknown error"); - - if(options.fields === undefined) options.fields = 20; - - //@ts-ignore TODO: find another way of handling IDs from types before inserting to database - const planet = { - owner: user, - name: options.name, - fields: options.fields, - resources: new Array, - buildings: new Array, - ships: new Array - } - - await (await Planets()).insertOne(planet); +export const getAllPlanets = async () => { + return await (await Planets()).find({}).toArray() as DBPlanet[]; } export const getPlanetById = async (id: ObjectId) => { - const planets = await Planets(); - const planet = await planets.findOne({ - _id: id - }); - - if(!planet) return null; - - planet.owner = await getUserById(planet.owner); - return planet as Planet; + return await (await Planets()).findOne({ _id: id }) as DBPlanet; } -export const createOrUpgradeBuilding = async (planetId: ObjectId, building: Building) => { - const planet = await getPlanetById(planetId); - if(!planet) throw new Error("Planet not found"); - - const buildingIndex = planet.buildings.findIndex(b => b.id === building.id); - if(buildingIndex === -1) { - planet.buildings.push(building); - } else { - planet.buildings[buildingIndex].level++; - } - +export const updatePlanetResources = async (planetId: ObjectId, resources: Array) => { const planets = await Planets(); - await planets.updateOne({ - _id: planetId - }, { + await planets.updateOne({ _id: planetId }, { $set: { - buildings: planet.buildings + resources } - }) + }); +} + +export const updatePlanetBuildings = async (planetId: ObjectId, buildings: Array<{ id: string, level: number }>) => { + const planets = await Planets(); + await planets.updateOne({ _id: planetId }, { + $set: { + buildings + } + }); } \ No newline at end of file diff --git a/src/lib/db/research.ts b/src/lib/db/research.ts index 01ccb31..78c7e9c 100644 --- a/src/lib/db/research.ts +++ b/src/lib/db/research.ts @@ -1,4 +1,4 @@ -import DBResearch from '../../types/DBResearch'; +import DBResearch from '../../types/db/DBResearch'; import { Research } from '../db/mongodb'; export const getAllResearch = async () => { diff --git a/src/lib/db/resources.ts b/src/lib/db/resources.ts index d487850..581af3f 100644 --- a/src/lib/db/resources.ts +++ b/src/lib/db/resources.ts @@ -1,4 +1,4 @@ -import DBResource from '../../types/DBResource'; +import DBResource from '../../types/db/DBResource'; import { Resources } from '../db/mongodb'; export const getAllResources = async () => { diff --git a/src/lib/db/sectors.ts b/src/lib/db/sectors.ts new file mode 100644 index 0000000..4b65e53 --- /dev/null +++ b/src/lib/db/sectors.ts @@ -0,0 +1,13 @@ +import { ObjectId } from "mongodb"; +import { Sectors } from "./mongodb"; +import DBSector from "../../types/db/DBSector"; + +export const getAllSectors = async () => { + return await (await Sectors()).find({}).toArray() as DBSector[]; +} + +export const getSectorById = async (id: ObjectId) => { + return await (await Sectors()).findOne({ + _id: id + }) as DBSector; +} \ No newline at end of file diff --git a/src/lib/db/ships.ts b/src/lib/db/ships.ts index 8ead886..558ac3c 100644 --- a/src/lib/db/ships.ts +++ b/src/lib/db/ships.ts @@ -1,4 +1,4 @@ -import DBShip from '../../types/DBShip'; +import DBShip from '../../types/db/DBShip'; import { Ships } from '../db/mongodb'; export const getAllShips = async () => { diff --git a/src/lib/db/systems.ts b/src/lib/db/systems.ts new file mode 100644 index 0000000..339b1eb --- /dev/null +++ b/src/lib/db/systems.ts @@ -0,0 +1,13 @@ +import { ObjectId } from "mongodb"; +import { Systems } from "./mongodb"; +import DBSystem from "../../types/db/DBSystem"; + +export const getAllSystems = async () => { + return await (await Systems()).find({}).toArray() as DBSystem[]; +} + +export const getSystemById = async (id: ObjectId) => { + return await (await Systems()).findOne({ + _id: id + }) as DBSystem; +} \ No newline at end of file diff --git a/src/lib/db/users.ts b/src/lib/db/users.ts index 6bcd4a6..5f9d586 100644 --- a/src/lib/db/users.ts +++ b/src/lib/db/users.ts @@ -1,33 +1,30 @@ -import { Planets, Users } from '../db/mongodb'; -import type User from '../../types/User'; -import type AccessToken from '../../types/AccessToken'; +import { Users } from '../db/mongodb'; import { ObjectId } from 'mongodb'; import { hash } from 'argon2' -import { createInitialResources } from '../utils/resourceManager'; -import type Research from '../../types/Research'; +import DBUser from '../../types/db/DBUser'; +import User from '../classes/User'; +import AccessToken from '../../types/AccessToken'; export const getAllUsers = async () => { const users = await Users(); - return users.find({}).toArray() as Promise; + return users.find({}).toArray() as Promise; } export const createUser = async (username: string, email: string, password: string) => { - const user: User = { + const user = { username, email, password: await hash(password), lastLogin: new Date(), createdAt: new Date(), updatedAt: new Date(), - //@ts-ignore - resources: {} + research: [], } await (await Users()).insertOne(user); const newUser = await getUserByNickOrEmail(username); if(!newUser) return user; - createInitialResources(newUser._id); return newUser; } @@ -40,7 +37,7 @@ export const getUserById = async (id: ObjectId) => { const users = await Users(); return users.findOne({ _id: id - }) as Promise; + }) as Promise; } export const getUserByNickOrEmail = async (searchString: string) => { @@ -50,14 +47,18 @@ export const getUserByNickOrEmail = async (searchString: string) => { { username: searchString }, { email: searchString } ] - }) as Promise; + }) as Promise; } -export const getUserByAccessToken = async(accessToken: string | AccessToken): Promise => { +export const getUserByAccessToken = async(accessToken: string | AccessToken): Promise => { if(typeof accessToken === "string") { const userId = new ObjectId(Buffer.from(accessToken.split(".")[2], 'base64url').toString()); return getUserById(userId); - } else return getUserById(accessToken.user as ObjectId) + } else { + const user = accessToken.user; + if(!user) return null; + return getUserById(user?.id); + } } export const updateLastLogin = async (user: User) => { @@ -65,54 +66,14 @@ export const updateLastLogin = async (user: User) => { return users.updateOne({ username: user.username }, { $set: { lastLogin: new Date() } }); } -export const getUserResearch = async (user: User): Promise> => { - const research: Array = []; - - if (user?.research !== undefined) { - user?.research.forEach((res: Research) => { - research.push(res); - }); - } - - return research; +export const getUserResearch = async (user: User): Promise> => { + const users = await Users(); + const rawUser = await users.findOne({ username: user.username }); + if (!rawUser) return []; + return rawUser.research; } -export const createOrUpgradeResearch = async (userId: ObjectId, research: Research) => { +export const updateUserResearch = async (user: User, research: Array<{ id: string, level: number }>) => { const users = await Users(); - - const result = await users.updateOne( - { _id: userId, "research.id": research.id }, - { $set: { "research.$.level": research.level } } - ); - - if (result.modifiedCount === 0) { - await users.updateOne({ _id: userId }, - { $push: { research } } - ); - } -} - -export const getUserWithPlanets = async (id: ObjectId): Promise => { - const users = await Users(); - const planets = await Planets(); - - const rawUser = await users.findOne({ - _id: id - }) - - if (!rawUser) return null; - - const userPlanets = (await planets.find({ - owner: id - }).toArray()).map(planet => { - planet.owner = rawUser; - return planet; - }); - - rawUser.planets = { - partial: false, - data: userPlanets - } - - return rawUser as User; + users.updateOne({ username: user.username }, { $set: { research } }); } \ No newline at end of file diff --git a/src/lib/utils/calculateAvailableResources.ts b/src/lib/utils/calculateAvailableResources.ts deleted file mode 100644 index 733274f..0000000 --- a/src/lib/utils/calculateAvailableResources.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type Resource from "../../types/Resource"; -import type Resources from "../../types/Resources"; - -export default function calculateAvailableResources(userResources: Array, buildingCost: Array) { - let canBuild = true; - const resources = {} as Array; - - userResources.forEach((userResource) => { - const resource = buildingCost.find((buildingResource) => buildingResource.name === userResource.name); - if (resource) { - resources.push({ - name: userResource.name, - amount: userResource.amount - resource.amount - }); - if (userResource.amount < resource.amount) canBuild = false; - } - }); - - return { - canBuild, - resources - }; -} \ No newline at end of file diff --git a/src/lib/utils/resourceManager.ts b/src/lib/utils/resourceManager.ts deleted file mode 100644 index ada3e3b..0000000 --- a/src/lib/utils/resourceManager.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { ObjectId } from "mongodb" -import { Planets } from "../db/mongodb"; -import type PlayerResource from "../../types/PlayerResource"; -import { getPlanetById } from "../db/planets"; - -export const createInitialResources = async (planetId: ObjectId) => { - const resources: Array = [ - { - name: "coal", - amount: 11, - lastUpdated: new Date(), - perHourMiningRate: 11 - }, - { - name: "iron", - amount: 22, - lastUpdated: new Date(), - perHourMiningRate: 22 - }, - { - name: "gold", - amount: 33, - lastUpdated: new Date(), - perHourMiningRate: 33 - }, - { - name: "water", - amount: 44, - lastUpdated: new Date(), - perHourMiningRate: 44 - }, - { - name: "sulfuricAcid", - amount: 55, - lastUpdated: new Date(), - perHourMiningRate: 55 - }, - { - name: "liquidNitrogen", - amount: 66, - lastUpdated: new Date(), - perHourMiningRate: 66 - }, - { - name: "hydrogen", - amount: 77, - lastUpdated: new Date(), - perHourMiningRate: 77 - }, - { - name: "oxygen", - amount: 88, - lastUpdated: new Date(), - perHourMiningRate: 88 - }, - { - name: "helium3", - amount: 99, - lastUpdated: new Date(), - perHourMiningRate: 99 - } - ]; - - updatePlanetResources(planetId, resources); -} - -export const getResourcesFromPlanet = async (planetId: ObjectId): Promise | null> => { - const planet = await getPlanetById(planetId); - - if(!planet) return null; - - return planet.resources; -} - -export const updatePlanetResources = async (planetId: ObjectId, resources: Array) => { - const planets = await Planets(); - await planets.updateOne({ _id: planetId }, { - $set: { - resources - } - }); -} - -export const calculateCurrentAvailableResources = async (planetId: ObjectId): Promise> => { - const resources = await getResourcesFromPlanet(planetId); - - if(resources === null) return []; - - resources.forEach(res => { - const timeDiff = Math.abs((new Date()).getTime() - res.lastUpdated.getTime()); - const hours = timeDiff / (1000 * 60 * 60); - const amountToAdd = hours * res.perHourMiningRate; - res.amount += amountToAdd; - res.lastUpdated = new Date(); - }); - - - updatePlanetResources(planetId, resources); - return resources; -} \ No newline at end of file diff --git a/src/lib/utils/validateAccessToken.ts b/src/lib/utils/validateAccessToken.ts index d943e37..cd0ec71 100644 --- a/src/lib/utils/validateAccessToken.ts +++ b/src/lib/utils/validateAccessToken.ts @@ -1,7 +1,8 @@ -import type { ObjectId } from "mongodb"; -import type AccessToken from "../../types/AccessToken"; +import { ObjectId } from "mongodb"; +import AccessToken from "../../types/AccessToken"; import { getAccessToken } from "../db/accessTokens"; import { getUserById } from "../db/users"; +import locationManager from "../classes/managers/LocationManager"; export default async function validateAccessToken(request: Request): Promise { let accessToken = request.url.split("?")[1]?.split("&").filter((x) => x.split("=")[0] === "token")[0].split("=")[1]; @@ -58,5 +59,12 @@ export default async function validateAccessToken(request: Request): Promise { const data = await request.json().catch(() => {return new Response( @@ -53,16 +53,25 @@ export const POST: APIRoute = async({ request }) => { const now = new Date(); const timestamp = Buffer.from(String(Date.now())).toString('base64url'); - const user = Buffer.from(userFromDb._id?.toString() ?? "").toString('base64url'); + const userEncoded = Buffer.from(userFromDb._id?.toString() ?? "").toString('base64url'); const random = randomBytes(16).toString("base64url"); const randomHashed = createHash("sha256").update(random).digest("hex"); const expiresIn = (data.duration ?? 86400) * 1000; - const tokenString = `A.${timestamp}.${user}.${random}`; + const tokenString = `A.${timestamp}.${userEncoded}.${random}`; + + const user = locationManager.getUser(userFromDb._id); + if(!user) return new Response( + JSON.stringify({ + code: 404, + message: "Not found", + error: `User ${data.username} not found` + }), { status: 404 } + ) const accessToken: AccessToken = { type: "A", - user: userFromDb._id as ObjectId, + user, entropy: randomHashed.toString(), createdAt: now, expiresAt: new Date(now.getTime() + expiresIn), diff --git a/src/pages/api/auth/testAccessToken.ts b/src/pages/api/auth/testAccessToken.ts index a6d80fc..54612a2 100644 --- a/src/pages/api/auth/testAccessToken.ts +++ b/src/pages/api/auth/testAccessToken.ts @@ -1,13 +1,20 @@ import type { APIRoute } from "astro"; import validateAccessToken from "../../../lib/utils/validateAccessToken"; import { getUserByAccessToken } from "../../../lib/db/users"; -import type User from "../../../types/User"; export const GET: APIRoute = async({ request }) => { const response = await validateAccessToken(request); if(response instanceof Response) return response; - const user = (await getUserByAccessToken(response)) as User; + const user = await getUserByAccessToken(response); + + if(!user) return new Response( + JSON.stringify({ + code: 404, + message: "Not found", + data: "Access token does not match any user" + }), { status: 404 } + ); return new Response( JSON.stringify({ diff --git a/src/pages/api/build/createBuilding.ts b/src/pages/api/build/createBuilding.ts index fea9d55..6dacf05 100644 --- a/src/pages/api/build/createBuilding.ts +++ b/src/pages/api/build/createBuilding.ts @@ -1,13 +1,10 @@ -import { build, type APIRoute } from "astro"; +import { type APIRoute } from "astro"; import validateAccessToken from "../../../lib/utils/validateAccessToken"; -import { calculateCurrentAvailableResources, updatePlanetResources } from "../../../lib/utils/resourceManager"; -import { getUserByAccessToken, getUserResearch } from "../../../lib/db/users"; -import Planet from "../../../types/Planet"; -import { createOrUpgradeBuilding, getPlanetById } from "../../../lib/db/planets"; +import { getUserByAccessToken } from "../../../lib/db/users"; import { ObjectId } from "mongodb"; -import DBResource from "../../../types/PlayerResource"; -import { getAllBuildings } from "../../../lib/db/buildings"; -import DBBuilding from "../../../types/DBBuilding"; +import locationManager from "../../../lib/classes/managers/LocationManager"; +import Building from "../../../lib/classes/Building"; +import { getAllResources } from "../../../lib/db/resources"; export const POST: APIRoute = async({ request }) => { const response = await validateAccessToken(request); @@ -36,120 +33,77 @@ export const POST: APIRoute = async({ request }) => { ) } - const buildingId = body.building; - let buildingData: DBBuilding | null = null; + const userPlanet = locationManager.getPlanet(new ObjectId(body.planet)); - const buildings = await getAllBuildings(); - - buildingData = buildings.find((element) => element.id === buildingId) ?? null; - - if(!buildingData) { - return new Response( - JSON.stringify({ - code: 400, - message: "Bad Request", - error: "Invalid building id" - }), { status: 400 } - ) - } - let userPlanet: Planet | null; - try { - userPlanet = await getPlanetById(new ObjectId(body.planetId)); - } catch(e) { - return new Response( - JSON.stringify({ - code: 400, - message: "Bad Request", - error: "Invalid planet id" - }), { status: 400 } - ) - } if(!userPlanet) { - return new Response( - JSON.stringify({ - code: 404, - message: "Not Found", - error: "Planet not found" - }), { status: 404 } - ) - } - - // check requirements - // buildings - const playerBuildings = userPlanet.buildings; - buildingData.requirements.buildings.forEach((buildingReq: any) => { - if(playerBuildings.filter((building) => building.id === buildingReq.id)[0].level < buildingReq.level) { - return new Response( - JSON.stringify({ - code: 400, - message: "Bad Request", - error: `${buildingReq.id} level ${buildingReq.level} required, found ${playerBuildings.filter((building) => building.id === buildingReq.id)[0].level}` - }), { status: 400 } - ) - } - }); - - // research - const playerResearch = await getUserResearch(user); - buildingData.requirements.research.forEach((researchReq: any) => { - if(playerResearch.filter((research) => research.id === researchReq.id)[0].level < researchReq.level) { - return new Response( - JSON.stringify({ - code: 400, - message: "Bad Request", - error: `${researchReq.id} level ${researchReq.level} required, found ${playerResearch.filter((research) => research.id === researchReq.id)[0].level}` - }), { status: 400 } - ) - } - }); - - // resources - const resources = await calculateCurrentAvailableResources(userPlanet._id); - if(!resources) { - return new Response( - JSON.stringify({ - code: 500, - message: "Internal Server Error", - error: "Failed to get resources" - }), { status: 500 } - ) - } - const playerCurrentResearch = playerResearch.filter((element: any) => element.id === buildingId)[0]; - const level = playerCurrentResearch ? playerCurrentResearch.level : 0; - const newResources = structuredClone(resources); - const missingResources: Array<{}> = []; - Object.entries(buildingData.requirements.resources).forEach(([key, value]) => { - const res = resources.filter((element: DBResource) => element.name === value.name)[0]; - const cost = playerCurrentResearch ? value.amount * Math.pow((buildingData as {multiplier: number}).multiplier, level) : value.amount; - - if(res.amount < cost) { - missingResources.push({ - name: key, - required: cost, - available: res.amount - }); - return; - } - else newResources.filter((element: DBResource) => element.name === value.name)[0].amount -= cost; - }); - - if(missingResources.length > 0) { return new Response( JSON.stringify({ code: 400, message: "Bad Request", - data: missingResources + error: "Invalid planet ID" }), { status: 400 } ) } - await updatePlanetResources(userPlanet._id, newResources); - await createOrUpgradeBuilding(userPlanet._id, { id: buildingId, level: level + 1 }); + const buildingId: string = body.building; + + const buildingObj = userPlanet.buildings.buildingsDB.find(b => b.id === buildingId); + + if(!buildingObj) return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Invalid building ID" + }), { status: 400 } + ) + + const building = new Building(userPlanet.buildings, buildingObj, 1); + + const requirements = await building.checkRequirements(); + const resources = await building.checkRequiredResources((userPlanet.buildings.getBuildingById(buildingId)?.level ?? 0) + 1); + + if(!requirements.canBuild || !resources) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: requirements.error + " | " + resources ? "" : "Not enough resources" + }), { status: 400 } + ) + } + + const resourcesDiff = await userPlanet.resources.getDifference(building.data.requirements.resources.map(res => { + return { + id: res.id, + amount: Math.pow(building.data.multiplier, (userPlanet.buildings.getBuildingById(buildingId)?.level ?? 0) + 1) * res.amount + } + })); + + const resourceDB = await getAllResources(); + const resourcesAfter = resourcesDiff.map(res => { + const data = resourceDB.find(r => r.id === res.id); + + if(!data) throw new Error("Resource not found"); + + return { + id: res.id, + amount: res.amount, + lastUpdated: res.lastUpdated, + perHourMiningRate: res.perHourMiningRate, + data + } + }); + + userPlanet.resources.update(resourcesAfter); + userPlanet.buildings.addBuilding(building); + + await userPlanet.buildings.sync(); + await userPlanet.resources.sync(); return new Response( JSON.stringify({ code: 200, message: "OK" }), { status: 200 } - ) + ); } \ No newline at end of file diff --git a/src/pages/api/planets/getAllPlanets.ts b/src/pages/api/planets/getAllPlanets.ts index 098fdb3..fefce1c 100644 --- a/src/pages/api/planets/getAllPlanets.ts +++ b/src/pages/api/planets/getAllPlanets.ts @@ -1,17 +1,21 @@ import { APIRoute } from "astro"; import { getAllPlanets } from "../../../lib/db/planets"; -import Planet from "../../../types/Planet"; import { ObjectId } from "mongodb"; +import locationManager from "../../../lib/classes/managers/LocationManager"; export const GET: APIRoute = async ({ request }) => { const planets = await getAllPlanets(); const response: Array<{planetId: ObjectId, ownerId: ObjectId, name: string}> = []; - planets.forEach((planet: Planet) => { + planets.forEach(planet => { + const playerPlanet = locationManager.getPlanet(planet._id); + + if(!playerPlanet) return; + response.push({ planetId: planet._id, - ownerId: planet.owner._id, + ownerId: playerPlanet.manager.owner.id, name: planet.name, }) }); diff --git a/src/pages/api/planets/getPlanet/[planetId].ts b/src/pages/api/planets/getPlanet/[planetId].ts index 2a166f3..153b4d7 100644 --- a/src/pages/api/planets/getPlanet/[planetId].ts +++ b/src/pages/api/planets/getPlanet/[planetId].ts @@ -1,10 +1,8 @@ import { APIRoute } from "astro"; -import { getPlanetById } from "../../../../lib/db/planets"; import { ObjectId } from "mongodb"; -import Planet from "../../../../types/Planet"; -import { calculateCurrentAvailableResources } from "../../../../lib/utils/resourceManager"; import validateAccessToken from "../../../../lib/utils/validateAccessToken"; import { getUserByAccessToken } from "../../../../lib/db/users"; +import locationManager from "../../../../lib/classes/managers/LocationManager"; export const GET: APIRoute = async ({ params, request }) => { const response = await validateAccessToken(request); @@ -28,36 +26,23 @@ export const GET: APIRoute = async ({ params, request }) => { }), { status: 400 } ); - let planet: Planet | null; + const planet = locationManager.getPlanet(new ObjectId(planetId)); - try { - planet = await getPlanetById(new ObjectId(planetId)); - if(!planet) return new Response( - JSON.stringify({ - code: 404, - message: "Not Found" - }), { status: 404 } - ); - } catch(e) { - return new Response( - JSON.stringify({ - code: 500, - message: "Internal Server Error" - }), { status: 500 } - ); - } + if(!planet) return new Response( + JSON.stringify({ + code: 500, + message: "Internal Server Error" + }), { status: 500 } + ); - if(planet.owner._id.toString() !== user._id.toString()) return new Response( + if(!planet.manager.owner.id.equals(user._id)) return new Response( JSON.stringify({ code: 403, message: "Forbidden" }), { status: 403 } ); - await calculateCurrentAvailableResources(planet._id); - - //@ts-ignore - delete planet.owner.password; + await locationManager.getPlanet(planet._id)?.resources.calculateCurrentAvailableResources(); return new Response( JSON.stringify({ diff --git a/src/pages/api/research/getResearch.ts b/src/pages/api/research/getResearch.ts index c8d580d..7ea04d5 100644 --- a/src/pages/api/research/getResearch.ts +++ b/src/pages/api/research/getResearch.ts @@ -1,6 +1,7 @@ import { type APIRoute } from "astro"; import validateAccessToken from "../../../lib/utils/validateAccessToken"; import { getUserByAccessToken, getUserResearch } from "../../../lib/db/users"; +import locationManager from "../../../lib/classes/managers/LocationManager"; export const GET: APIRoute = async({ request }) => { const response = await validateAccessToken(request); @@ -16,11 +17,13 @@ export const GET: APIRoute = async({ request }) => { ) } + const userResearch = locationManager.getUser(user._id)?.research.research ?? []; + return new Response( JSON.stringify({ code: 200, message: "OK", - data: await getUserResearch(user) + data: userResearch }), { status: 200 } ) } \ No newline at end of file diff --git a/src/pages/api/research/performResearch.ts b/src/pages/api/research/performResearch.ts index be1b00d..4c598a9 100644 --- a/src/pages/api/research/performResearch.ts +++ b/src/pages/api/research/performResearch.ts @@ -1,20 +1,16 @@ import { type APIRoute } from "astro"; import validateAccessToken from "../../../lib/utils/validateAccessToken"; -import { createOrUpgradeResearch, getUserByAccessToken, getUserResearch } from "../../../lib/db/users"; -import { getPlanetById } from "../../../lib/db/planets"; -import Planet from "../../../types/Planet"; +import { getUserByAccessToken } from "../../../lib/db/users"; import { ObjectId } from "mongodb"; -import { calculateCurrentAvailableResources, getResourcesFromPlanet, updatePlanetResources } from "../../../lib/utils/resourceManager"; -import PlayerResource from "../../../types/PlayerResource"; -import calculateAvailableResources from "../../../lib/utils/calculateAvailableResources"; -import { getAllResearch, getResearchById } from "../../../lib/db/research"; +import { getResearchById } from "../../../lib/db/research"; +import locationManager from "../../../lib/classes/managers/LocationManager"; export const POST: APIRoute = async({ request }) => { const response = await validateAccessToken(request); if(response instanceof Response) return response; - const user = await getUserByAccessToken(response); - if(user === null) { + const userDB = await getUserByAccessToken(response); + if(userDB === null) { return new Response( JSON.stringify({ code: 401, @@ -36,8 +32,30 @@ export const POST: APIRoute = async({ request }) => { ) } + const userPlanet = locationManager.getPlanet(new ObjectId(body.planetId)); + if(!userPlanet) { + return new Response( + JSON.stringify({ + code: 404, + message: "Not Found", + error: "Planet not found" + }), { status: 404 } + ) + } + + const user = userPlanet.manager.owner; + + if(!user.id.equals(userDB._id)) { + return new Response( + JSON.stringify({ + code: 403, + message: "Forbidden", + error: "You don't own this planet" + }), { status: 403 } + ) + } - const researchId = body.research; + const researchId = body.research as string; const research = await getResearchById(researchId); if(!research) { @@ -49,47 +67,27 @@ export const POST: APIRoute = async({ request }) => { }), { status: 400 } ) } - let userPlanet: Planet | null; - try { - userPlanet = await getPlanetById(new ObjectId(body.planetId)); - } catch(e) { - return new Response( - JSON.stringify({ - code: 400, - message: "Bad Request", - error: "Invalid planet id" - }), { status: 400 } - ) - } - if(!userPlanet) { - return new Response( - JSON.stringify({ - code: 404, - message: "Not Found", - error: "Planet not found" - }), { status: 404 } - ) - } + // check requirements // buildings const buildings = userPlanet.buildings; - research.requirements.buildings.forEach((buildingReq) => { - if(buildings.filter((building) => building.id === buildingReq.id)[0]?.level ?? 0 < buildingReq.level) { + for(const buildingReq of research.requirements.buildings) { + if((buildings.buildings.find((building) => building.data.id === buildingReq.id)?.level ?? 0) < buildingReq.level) { return new Response( JSON.stringify({ code: 400, message: "Bad Request", - error: `${buildingReq.id} level ${buildingReq.level} required, found ${buildings.filter((building) => building.id === buildingReq.id)[0]?.level ?? 0}` + error: `${buildingReq.id} level ${buildingReq.level} required, found ${buildings.buildings.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0}` }), { status: 400 } ) } - }); + }; // research - const playerResearch = await getUserResearch(user); - research.requirements.research.forEach((researchReq) => { - if(playerResearch.filter((research) => research.id === researchReq.id)[0].level < researchReq.level) { + const playerResearch = user.research.research; + for(const researchReq of research.requirements.research) { + if((playerResearch.find((research) => research.id === researchReq.id)?.level ?? 0) < researchReq.level) { return new Response( JSON.stringify({ code: 400, @@ -98,10 +96,10 @@ export const POST: APIRoute = async({ request }) => { }), { status: 400 } ) } - }); + }; // resources - const resources = await calculateCurrentAvailableResources(userPlanet._id); + const resources = await userPlanet.resources.calculateCurrentAvailableResources(); if(!resources) { return new Response( JSON.stringify({ @@ -111,24 +109,25 @@ export const POST: APIRoute = async({ request }) => { }), { status: 500 } ) } + const playerCurrentResearch = playerResearch.filter((element: any) => element.id === researchId)[0]; const level = playerCurrentResearch ? playerCurrentResearch.level : 0; const newResources = structuredClone(resources); - const missingResources: Array<{}> = []; - research.requirements.resources.forEach(resource => { - const res = resources.filter((element: PlayerResource) => element.name === resource.name)[0]; + const missingResources: Array<{ id: string, required: number, available: number }> = []; + for(const resource of research.requirements.resources) { + const res = resources.filter((element) => element.id === resource.id)[0]; const cost = playerCurrentResearch ? resource.amount * Math.pow(research.multiplier, level) : resource.amount; if(res.amount < cost) { missingResources.push({ - name: resource.name, + id: resource.id, required: cost, available: res.amount }); - return; + break; } - else newResources.filter((element: PlayerResource) => element.name === resource.name)[0].amount -= cost; - }); + else newResources.filter((element) => element.id === resource.id)[0].amount -= cost; + }; if(missingResources.length > 0) { return new Response( @@ -139,9 +138,16 @@ export const POST: APIRoute = async({ request }) => { }), { status: 400 } ) } + + userPlanet.resources.update(newResources); + userPlanet.manager.owner.research.addResearch({ + id: researchId, + level: level + 1, + data: research + }); - await updatePlanetResources(userPlanet._id, newResources); - await createOrUpgradeResearch(user._id, { id: researchId, level: level + 1 }); + await userPlanet.resources.sync(); + await userPlanet.manager.owner.research.sync(); return new Response( JSON.stringify({ diff --git a/src/pages/game/buildings.astro b/src/pages/game/buildings.astro index 530be72..7a4ebfc 100644 --- a/src/pages/game/buildings.astro +++ b/src/pages/game/buildings.astro @@ -6,7 +6,9 @@ import { getUserByAccessToken } from '../../lib/db/users'; import { getHighestWeightedLanguage, getLocales, getName, getObj } from '../../lib/utils/langDriver'; import ResourceBar from '../../components/ResourceBar.astro'; import { getAllBuildings } from '../../lib/db/buildings'; -import DBBuilding from '../../types/DBBuilding'; +import locationManager from '../../lib/classes/managers/LocationManager'; +import { ObjectId } from 'mongodb'; +import DBBuilding from '../../types/db/DBBuilding'; const buildingsList = await getAllBuildings(); @@ -21,20 +23,31 @@ const locale = await getHighestWeightedLanguage(Astro.request.headers.get('accep const lang = await getLocales(locale); +const planetId = Astro.cookies.get('planetid')?.value ?? ""; +if(planetId === "") return "No planet selected"; + +const planet = locationManager.getPlanet(new ObjectId(planetId)); +if(!planet) return "Planet not found"; + const modalSet: { [key: string]: { resources: Array, research: Array, buildings: Array, energy: number } } = {}; for(const building of buildingsList) { modalSet[building.id] = { - resources: building.requirements.resources, + resources: building.requirements.resources.map(resource => { + return { + id: resource.id, + amount: Math.pow(building.multiplier, (planet.buildings.getBuildingById(building.id)?.level ?? 0) ) * resource.amount + }; + }), research: building.requirements.research, buildings: building.requirements.buildings, energy: building.energy }; } -const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: DBBuilding[] }, building) => { +const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: Array }, building) => { if(!acc[building.category]) acc[building.category] = []; - acc[building.category].push(building); + acc[building.category].push({ ...building, level: planet.buildings.getBuildingById(building.id)?.level ?? 0 }); return acc; }, {}); --- @@ -60,6 +73,7 @@ const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: DBBuildi {buildings.map(building => ( - \ No newline at end of file diff --git a/src/pages/game/research.astro b/src/pages/game/research.astro index 5d96e87..f696d37 100644 --- a/src/pages/game/research.astro +++ b/src/pages/game/research.astro @@ -5,7 +5,6 @@ import { getUserByAccessToken } from '../../lib/db/users'; import { getHighestWeightedLanguage, getLocales, getName, getObj } from '../../lib/utils/langDriver'; import ResourceBar from '../../components/ResourceBar.astro'; import ItemCard from '../../components/ItemCard.astro'; -import DBResearch from '../../types/DBResearch'; import { getAllResearch } from '../../lib/db/research'; const researchList = await getAllResearch(); @@ -17,23 +16,13 @@ if(loggedToken === null || username === "") return Astro.redirect('/logout'); const checkUser = await getUserByAccessToken(loggedToken); if(checkUser === null || checkUser.username !== username) return Astro.redirect('/logout'); +const planetId = Astro.cookies.get('planetid')?.value ?? ""; +if(planetId === "") return Astro.redirect('/logout'); + const locale = await getHighestWeightedLanguage(Astro.request.headers.get('accept-language')); const lang = await getLocales(locale); -// type DBResearchDetails = DBResearch & { level: number }; -// const researchDetails: DBResearchDetails[] = []; -// researchList.forEach(element => { -// const userLevel = checkUser.research.find(x => x.id === element.id)?.level ?? 0; -// const tempResDetails: DBResearchDetails = { -// level: userLevel, -// ...element -// } - -// researchDetails.push(tempResDetails); -// }); - - const modalSet: { [key: string]: { resources: Array, research: Array, buildings: Array } } = {}; for(const research of researchList) { @@ -66,6 +55,7 @@ for(const research of researchList) { x.id === research.id)?.level.toString() ?? "0"} description={getObj(lang, "research", research.id).description ?? ""} image={`/images/research/${research.id}.jpeg`} button_type="general" @@ -187,7 +177,7 @@ for(const research of researchList) { z-index: 101; } - \ No newline at end of file diff --git a/src/pages/login.astro b/src/pages/login.astro index 6b99c65..59d8b93 100644 --- a/src/pages/login.astro +++ b/src/pages/login.astro @@ -2,11 +2,12 @@ import Layout from '../layouts/Layout.astro'; import NavBar from '../components/NavBar.astro'; -import { getUserByNickOrEmail, getUserWithPlanets, updateLastLogin } from '../lib/db/users'; +import { getUserByNickOrEmail, updateLastLogin } from '../lib/db/users'; import config from '../../config.json'; import { verify } from 'argon2'; +import locationManager from '../lib/classes/managers/LocationManager'; let error = ""; @@ -25,9 +26,11 @@ if(Astro.request.method === "POST") { Astro.redirect("/login"); } - const user = await getUserByNickOrEmail(username as string); + const userDB = await getUserByNickOrEmail(username as string); - if(user !== null && await verify(user.password, password as string)) { + if(userDB !== null && await verify(userDB.password, password as string)) { + const user = locationManager.getUser(userDB._id); + if(!user) throw new Error("User not found"); const sessionTime = config.SESSION_TIME_MINUTES * 60; const res = await fetch(`http://localhost:4321/api/auth/generateAccessToken`, { @@ -61,18 +64,14 @@ if(Astro.request.method === "POST") { secure: true }); - Astro.cookies.set("userid", user._id?.toString() as string, { + Astro.cookies.set("userid", user.id?.toString() as string, { path: "/", maxAge: sessionTime, sameSite: "lax", secure: true }); - const userWithPlanets = await getUserWithPlanets(user._id); - if(!userWithPlanets) return; - const planetId = userWithPlanets.planets.data[0]._id; - - Astro.cookies.set("planetid", planetId.toString(), { + Astro.cookies.set("planetid", user.mainPlanet._id, { path: "/", maxAge: sessionTime, sameSite: "lax", diff --git a/src/types/AccessToken.ts b/src/types/AccessToken.ts index aa022d2..b95cbce 100644 --- a/src/types/AccessToken.ts +++ b/src/types/AccessToken.ts @@ -1,11 +1,10 @@ -import type { ObjectId } from "mongodb"; +import User from "../lib/classes/User"; export default interface AccessToken { - _id?: ObjectId; - type: "A" | "X"; - user: ObjectId | null; + type: string; + user: User | null; entropy: string; createdAt: Date; expiresAt: Date | null; - createdFrom: null | 'loginForm'; + createdFrom: string | null; } \ No newline at end of file diff --git a/src/types/Building.ts b/src/types/Building.ts deleted file mode 100644 index 9835ac8..0000000 --- a/src/types/Building.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface Building { - id: string; - level: number; -} diff --git a/src/types/DBBuilding.ts b/src/types/DBBuilding.ts deleted file mode 100644 index 28cd1fd..0000000 --- a/src/types/DBBuilding.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default interface DBBuilding { - id: string, - category: string, - requirements: { - buildings: Array<{ id: string, level: number }>, - research: Array<{ id: string, level: number }>, - resources: Array<{ name: string, amount: number }>, - }, - energy: number, - multiplier: number, -} \ No newline at end of file diff --git a/src/types/DBResearch.ts b/src/types/DBResearch.ts deleted file mode 100644 index 9ca0237..0000000 --- a/src/types/DBResearch.ts +++ /dev/null @@ -1,10 +0,0 @@ -export default interface DBResearch { - id: string, - requirements: { - buildings: Array<{ id: string, level: number }>, - research: Array<{ id: string, level: number }>, - resources: Array<{ name: string, amount: number }>, - }, - time: number, - multiplier: number, -} \ No newline at end of file diff --git a/src/types/DBResource.ts b/src/types/DBResource.ts deleted file mode 100644 index 8efce1d..0000000 --- a/src/types/DBResource.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default interface DBResource { - id: string, - type: string, - icon: string -} \ No newline at end of file diff --git a/src/types/DBShip.ts b/src/types/DBShip.ts deleted file mode 100644 index 688bcc7..0000000 --- a/src/types/DBShip.ts +++ /dev/null @@ -1,20 +0,0 @@ -export default interface DBShip { - id: string, - capacity: { - solid: number, - liquid: number, - gas: number, - }, - requirements: { - buildings: Array<{ id: string, level: number }>, - research: Array<{ id: string, level: number }>, - resources: Array<{ name: string, amount: number }>, - }, - time: number, - structure: { - hitpoints: number, - defense: number, - attack: number, - }, - speed: number, -} \ No newline at end of file diff --git a/src/types/Planet.ts b/src/types/Planet.ts deleted file mode 100644 index d12d56d..0000000 --- a/src/types/Planet.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { ObjectId } from "mongodb"; -import type PlayerResource from "./PlayerResource"; -import type Building from "./Building"; -import type Ship from "./Ship"; -import type User from "./User"; - -export default interface Planet { - _id: ObjectId, - owner: User, - name: string, - fields: number, - resources: Array, - buildings: Array, - ships: Array, -} \ No newline at end of file diff --git a/src/types/PlayerResource.ts b/src/types/PlayerResource.ts deleted file mode 100644 index f8da010..0000000 --- a/src/types/PlayerResource.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type Resource from "./Resource"; - -export default interface DBResource extends Resource { - lastUpdated: Date; - perHourMiningRate: number; -} \ No newline at end of file diff --git a/src/types/Research.ts b/src/types/Research.ts deleted file mode 100644 index f9ac66b..0000000 --- a/src/types/Research.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface Research { - id: string; - level: number; -} \ No newline at end of file diff --git a/src/types/Resource.ts b/src/types/Resource.ts deleted file mode 100644 index 2354483..0000000 --- a/src/types/Resource.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface Resource { - name: string; - amount: number; -} \ No newline at end of file diff --git a/src/types/Resources.ts b/src/types/Resources.ts deleted file mode 100644 index c986ec6..0000000 --- a/src/types/Resources.ts +++ /dev/null @@ -1,13 +0,0 @@ -export default interface Resources { - coal: number; - iron: number; - gold: number; - - water: number; - sulfuricAcid: number; - liquidNitrogen: number; - - hydrogen: number; - oxygen: number; - helium3: number; -} diff --git a/src/types/Ship.ts b/src/types/Ship.ts deleted file mode 100644 index 3515b7f..0000000 --- a/src/types/Ship.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface Ship { - id: string; - amount: number; -} \ No newline at end of file diff --git a/src/types/User.ts b/src/types/User.ts deleted file mode 100644 index c1d4244..0000000 --- a/src/types/User.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { ObjectId } from "mongodb"; -import type Research from "./Research"; -import type Planet from "./Planet"; - -export default interface User { - _id: ObjectId; - username: string; - email: string; - password: string; - lastLogin: Date; - research: Array; - planets: { - partial: boolean; - data: Array; - }; - createdAt: Date; - updatedAt: Date; -} \ No newline at end of file diff --git a/src/types/db/DBAccessToken.ts b/src/types/db/DBAccessToken.ts new file mode 100644 index 0000000..f94ec4f --- /dev/null +++ b/src/types/db/DBAccessToken.ts @@ -0,0 +1,11 @@ +import { ObjectId } from "mongodb"; + +export default interface DBAccessToken { + _id: ObjectId; + type: string; + user: ObjectId; + entropy: string; + createdAt: Date; + expiresAt: Date; + createdFrom: string | null; +} \ No newline at end of file diff --git a/src/types/db/DBBuilding.ts b/src/types/db/DBBuilding.ts new file mode 100644 index 0000000..cad6525 --- /dev/null +++ b/src/types/db/DBBuilding.ts @@ -0,0 +1,15 @@ +import { ObjectId } from "mongodb"; + +export default interface DBBuilding { + _id: ObjectId; + id: string; + category: string; + requirements: { + buildings: Array<{ id: string, level: number }>, + research: Array<{ id: string, level: number }>, + resources: Array<{ id: string, amount: number }>, + }; + energy: number; + time: number; + multiplier: number; +} \ No newline at end of file diff --git a/src/types/db/DBFleet.ts b/src/types/db/DBFleet.ts new file mode 100644 index 0000000..742712d --- /dev/null +++ b/src/types/db/DBFleet.ts @@ -0,0 +1,11 @@ +import { ObjectId } from "mongodb"; + +export default interface DBFleet { + _id: ObjectId; + source: ObjectId; + destination: ObjectId; + finished: boolean; + returning: boolean; + mission: string; + ships: Array<{ id: string, amount: number }>; +} \ No newline at end of file diff --git a/src/types/db/DBGalaxy.ts b/src/types/db/DBGalaxy.ts new file mode 100644 index 0000000..74fe2a5 --- /dev/null +++ b/src/types/db/DBGalaxy.ts @@ -0,0 +1,7 @@ +import { ObjectId } from "mongodb"; + +export default interface DBGalaxy { + _id: ObjectId; + name: string; + sectors: Array; +} \ No newline at end of file diff --git a/src/types/db/DBPlanet.ts b/src/types/db/DBPlanet.ts new file mode 100644 index 0000000..0d17a64 --- /dev/null +++ b/src/types/db/DBPlanet.ts @@ -0,0 +1,10 @@ +import { ObjectId } from "mongodb"; + +export default interface DBPlanet { + _id: ObjectId; + name: string; + fields: number; + resources: Array<{ id: string, amount: number, lastUpdated: Date, perHourMiningRate: number }>; + buildings: Array<{ id: string, level: number }>; + ships: Array<{ id: string, amount: number }>; +} \ No newline at end of file diff --git a/src/types/db/DBResearch.ts b/src/types/db/DBResearch.ts new file mode 100644 index 0000000..58cde23 --- /dev/null +++ b/src/types/db/DBResearch.ts @@ -0,0 +1,13 @@ +import { ObjectId } from "mongodb"; + +export default interface DBResearch { + _id: ObjectId; + id: string; + requirements: { + buildings: Array<{ id: string, level: number }>, + research: Array<{ id: string, level: number }>, + resources: Array<{ id: string, amount: number }>, + }; + time: number; + multiplier: number; +} \ No newline at end of file diff --git a/src/types/db/DBResource.ts b/src/types/db/DBResource.ts new file mode 100644 index 0000000..fa2eaad --- /dev/null +++ b/src/types/db/DBResource.ts @@ -0,0 +1,8 @@ +import { ObjectId } from "mongodb"; + +export default interface DBResource { + _id: ObjectId; + id: string; + type: string; + icon: string; +} \ No newline at end of file diff --git a/src/types/db/DBSector.ts b/src/types/db/DBSector.ts new file mode 100644 index 0000000..65bf849 --- /dev/null +++ b/src/types/db/DBSector.ts @@ -0,0 +1,8 @@ +import { ObjectId } from "mongodb"; + +export default interface DBSector { + _id: ObjectId; + name: string; + expedition: any; // TODO: Define expedition type + systems: Array; +} \ No newline at end of file diff --git a/src/types/db/DBShip.ts b/src/types/db/DBShip.ts new file mode 100644 index 0000000..0c42b4d --- /dev/null +++ b/src/types/db/DBShip.ts @@ -0,0 +1,23 @@ +import { ObjectId } from "mongodb"; + +export default interface DBShip { + _id: ObjectId; + id: string; + capacity: { + solid: number; + liquid: number; + gas: number; + }; + requirements: { + buildings: Array<{ id: string, level: number }>, + research: Array<{ id: string, level: number }>, + resources: Array<{ id: string, amount: number }>, + }; + time: number; + structure: { + hitpoints: number; + defense: number; + attack: number; + }; + speed: number; +} \ No newline at end of file diff --git a/src/types/db/DBSystem.ts b/src/types/db/DBSystem.ts new file mode 100644 index 0000000..9b3030d --- /dev/null +++ b/src/types/db/DBSystem.ts @@ -0,0 +1,8 @@ +import { ObjectId } from "mongodb"; + +export default interface DBSystem { + _id: ObjectId; + name: string; + ownedBy: ObjectId; + planets: Array; +} \ No newline at end of file diff --git a/src/types/db/DBUser.ts b/src/types/db/DBUser.ts new file mode 100644 index 0000000..ea48cfe --- /dev/null +++ b/src/types/db/DBUser.ts @@ -0,0 +1,13 @@ +import { ObjectId } from "mongodb"; + +export default interface DBUser { + _id: ObjectId; + username: string; + email: string; + password: string; + lastLogin: Date; + createdAt: Date; + updatedAt: Date; + research: Array<{ id: string, level: number }>; + mainPlanet: ObjectId; +} \ No newline at end of file